Documentation
¶
Overview ¶
Package widget provides core widget types and interfaces for the gogpu/ui toolkit.
This package defines the fundamental building blocks for creating user interfaces: the Widget interface, WidgetBase helper struct, Context for accessing UI state, and Canvas for drawing operations.
Core Types ¶
- Widget: Interface that all UI elements must implement
- WidgetBase: Embeddable struct providing common widget functionality
- Context: Interface for accessing UI state during layout/draw/event handling
- Canvas: Interface for drawing operations (placeholder for render package)
Widget Interface ¶
The Widget interface is the foundation of the UI framework. Every UI element implements this interface to participate in layout, drawing, and event handling:
type Widget interface {
Layout(ctx Context, constraints Constraints) Size
Draw(ctx Context, canvas Canvas)
Event(ctx Context, e event.Event) bool
Children() []Widget
}
Using WidgetBase ¶
WidgetBase provides common functionality that most widgets need. Embed it in your custom widget implementations:
type MyButton struct {
widget.WidgetBase
label string
}
func NewMyButton(label string) *MyButton {
b := &MyButton{label: label}
b.SetVisible(true)
b.SetEnabled(true)
return b
}
Layout System ¶
Layout follows Flutter's box constraints model:
- Parent passes constraints to child via Layout()
- Child calculates its size within those constraints
- Child returns its chosen size to parent
- Parent positions child (sets bounds via SetBounds())
Example layout implementation:
func (w *MyWidget) Layout(ctx widget.Context, c geometry.Constraints) geometry.Size {
// Calculate preferred size
preferred := geometry.Sz(100, 50)
// Constrain to allowed range
return c.Constrain(preferred)
}
Event Handling ¶
Events flow through the widget tree. The Event method returns true if the event was consumed:
func (w *MyWidget) Event(ctx widget.Context, e event.Event) bool {
switch ev := e.(type) {
case *event.MouseEvent:
if ev.MouseType == event.MousePress {
w.handleClick(ev.Position)
return true
}
}
return false
}
Thread Safety ¶
Widgets are NOT thread-safe. All widget operations must occur on the main/UI thread. The Context interface provides the only safe way to communicate with the UI system from callbacks.
Design Principles ¶
- Composition over inheritance: Embed WidgetBase, don't subclass
- Explicit state: Widget state is explicit, not implicit
- Zero-allocation hot paths: Layout and draw should not allocate
- Interface-based: Depend on interfaces, not concrete types
Index ¶
- Variables
- func ClearRedrawInTree(w Widget)
- func MarkRedrawInTree(w Widget)
- func MountTree(w Widget, ctx Context)
- func NeedsRedrawInTree(w Widget) bool
- func StampScreenOrigin(child Widget, canvas Canvas)
- func UnmountTree(w Widget)
- type Canvas
- type Color
- type Context
- type ContextImpl
- func (c *ContextImpl) ClearInvalidation()
- func (c *ContextImpl) Cursor() CursorType
- func (c *ContextImpl) DeltaTime() time.Duration
- func (c *ContextImpl) FocusedWidget() Widget
- func (c *ContextImpl) Invalidate()
- func (c *ContextImpl) InvalidateRect(r geometry.Rect)
- func (c *ContextImpl) InvalidatedRect() geometry.Rect
- func (c *ContextImpl) IsFocused(w Widget) bool
- func (c *ContextImpl) IsInvalidated() bool
- func (c *ContextImpl) Now() time.Time
- func (c *ContextImpl) OverlayManager() OverlayManager
- func (c *ContextImpl) ReleaseFocus(w Widget)
- func (c *ContextImpl) RequestFocus(w Widget)
- func (c *ContextImpl) ResetCursor()
- func (c *ContextImpl) Scale() float32
- func (c *ContextImpl) Scheduler() SchedulerRef
- func (c *ContextImpl) SetCursor(cursor CursorType)
- func (c *ContextImpl) SetNow(now time.Time)
- func (c *ContextImpl) SetOnInvalidate(callback func())
- func (c *ContextImpl) SetOnInvalidateRect(callback func(geometry.Rect))
- func (c *ContextImpl) SetOverlayManager(om OverlayManager)
- func (c *ContextImpl) SetScale(scale float32)
- func (c *ContextImpl) SetScheduler(s SchedulerRef)
- func (c *ContextImpl) SetThemeProvider(tp ThemeProvider)
- func (c *ContextImpl) SetWindowSize(size geometry.Size)
- func (c *ContextImpl) ThemeProvider() ThemeProvider
- func (c *ContextImpl) WindowSize() geometry.Size
- type CursorType
- type DrawFunc
- type DrawStats
- type EventFunc
- type Focusable
- type LayoutFunc
- type Lifecycle
- type OverlayManager
- type SchedulerRef
- type Stopper
- type TextAlign
- type ThemeProvider
- type Unbinder
- type Widget
- type WidgetBase
- func (w *WidgetBase) AddBinding(b Unbinder)
- func (w *WidgetBase) AddChild(child Widget)
- func (w *WidgetBase) AddEffect(e Stopper)
- func (w *WidgetBase) Bounds() geometry.Rect
- func (w *WidgetBase) ChildAt(index int) Widget
- func (w *WidgetBase) ChildCount() int
- func (w *WidgetBase) Children() []Widget
- func (w *WidgetBase) CleanupBindings()
- func (w *WidgetBase) ClearChildren()
- func (w *WidgetBase) ClearRedraw()
- func (w *WidgetBase) ContainsPoint(p geometry.Point) bool
- func (w *WidgetBase) GlobalToLocal(p geometry.Point) geometry.Point
- func (w *WidgetBase) HasChildren() bool
- func (w *WidgetBase) ID() string
- func (w *WidgetBase) InsertChild(index int, child Widget)
- func (w *WidgetBase) IsEnabled() bool
- func (w *WidgetBase) IsFocused() bool
- func (w *WidgetBase) IsMounted() bool
- func (w *WidgetBase) IsVisible() bool
- func (w *WidgetBase) LocalToGlobal(p geometry.Point) geometry.Point
- func (w *WidgetBase) NeedsRedraw() bool
- func (w *WidgetBase) Parent() Widget
- func (w *WidgetBase) Position() geometry.Point
- func (w *WidgetBase) RemoveChild(child Widget) bool
- func (w *WidgetBase) RemoveChildAt(index int) Widget
- func (w *WidgetBase) ScreenBounds() geometry.Rect
- func (w *WidgetBase) ScreenOrigin() geometry.Point
- func (w *WidgetBase) SetBounds(bounds geometry.Rect)
- func (w *WidgetBase) SetEnabled(enabled bool)
- func (w *WidgetBase) SetFocused(focused bool)
- func (w *WidgetBase) SetID(id string)
- func (w *WidgetBase) SetMounted(m bool)
- func (w *WidgetBase) SetNeedsRedraw(v bool)
- func (w *WidgetBase) SetParent(parent Widget)
- func (w *WidgetBase) SetScreenOrigin(origin geometry.Point)
- func (w *WidgetBase) SetVisible(visible bool)
- func (w *WidgetBase) Size() geometry.Size
Constants ¶
This section is empty.
Variables ¶
var ( // ColorTransparent is fully transparent (alpha = 0). ColorTransparent = Color{R: 0, G: 0, B: 0, A: 0} // ColorBlack is solid black. ColorBlack = Color{R: 0, G: 0, B: 0, A: 1} // ColorWhite is solid white. ColorWhite = Color{R: 1, G: 1, B: 1, A: 1} // ColorRed is solid red. ColorRed = Color{R: 1, G: 0, B: 0, A: 1} // ColorGreen is solid green. ColorGreen = Color{R: 0, G: 1, B: 0, A: 1} // ColorBlue is solid blue. ColorBlue = Color{R: 0, G: 0, B: 1, A: 1} // ColorYellow is solid yellow. ColorYellow = Color{R: 1, G: 1, B: 0, A: 1} // ColorCyan is solid cyan. ColorCyan = Color{R: 0, G: 1, B: 1, A: 1} // ColorMagenta is solid magenta. ColorMagenta = Color{R: 1, G: 0, B: 1, A: 1} // ColorGray is medium gray (50% brightness). ColorGray = Color{R: 0.5, G: 0.5, B: 0.5, A: 1} // ColorLightGray is light gray (75% brightness). ColorLightGray = Color{R: 0.75, G: 0.75, B: 0.75, A: 1} // ColorDarkGray is dark gray (25% brightness). ColorDarkGray = Color{R: 0.25, G: 0.25, B: 0.25, A: 1} )
Common color constants.
Functions ¶
func ClearRedrawInTree ¶
func ClearRedrawInTree(w Widget)
ClearRedrawInTree clears the needsRedraw flag on all widgets in the subtree rooted at w.
This is called after a successful draw pass to mark the entire tree as visually up to date. The next frame will skip rendering unless a signal change marks widgets dirty again.
func MarkRedrawInTree ¶
func MarkRedrawInTree(w Widget)
MarkRedrawInTree sets the needsRedraw flag on all widgets in the subtree rooted at w.
This is used when a full redraw is required, such as after SetRoot, theme changes, or window resize. It ensures the next draw pass will render all widgets.
func MountTree ¶
MountTree recursively mounts all widgets in the subtree rooted at w. For each widget that implements Lifecycle, Mount(ctx) is called. Widgets already mounted are skipped.
func NeedsRedrawInTree ¶
NeedsRedrawInTree reports whether any widget in the subtree rooted at w needs re-rendering.
This walks the tree starting from w, checking each widget's NeedsRedraw flag. Returns true as soon as any dirty widget is found, false if the entire subtree is clean.
This is used by the retained-mode rendering system to determine whether a draw pass is necessary. When NeedsRedrawInTree returns false for the root widget, the entire frame can be skipped because the previous frame's output is still valid in the GPU framebuffer.
Widgets that do not embed WidgetBase (and thus lack the NeedsRedraw method) are treated as always needing redraw, ensuring correct rendering for custom widget implementations.
func StampScreenOrigin ¶
StampScreenOrigin computes and records the screen-space origin on a widget using the canvas's current transform offset and the widget's local bounds.
This should be called by container widgets in their Draw method, after calling [Canvas.PushTransform] for the container's position, and before calling Draw on each child widget. The framework also calls this for the root widget before the first Draw.
The screen origin accounts for all accumulated parent transforms including scroll offsets, making it correct for overlay positioning.
Example usage in a container's Draw method:
canvas.PushTransform(bounds.Min)
for _, child := range children {
widget.StampScreenOrigin(child, canvas)
child.Draw(ctx, canvas)
}
canvas.PopTransform()
func UnmountTree ¶
func UnmountTree(w Widget)
UnmountTree recursively unmounts all widgets in the subtree rooted at w. For each widget, bindings are cleaned up and Unmount() is called if implemented. Children are unmounted first (bottom-up).
Types ¶
type Canvas ¶
type Canvas interface {
// Clear fills the entire canvas with the given color.
Clear(color Color)
// DrawRect fills a rectangle with the given color.
DrawRect(r geometry.Rect, color Color)
// StrokeRect draws the outline of a rectangle.
//
// The strokeWidth specifies the line thickness in logical pixels.
StrokeRect(r geometry.Rect, color Color, strokeWidth float32)
// DrawRoundRect fills a rounded rectangle with the given color.
//
// The radius specifies the corner radius.
DrawRoundRect(r geometry.Rect, color Color, radius float32)
// StrokeRoundRect draws the outline of a rounded rectangle.
StrokeRoundRect(r geometry.Rect, color Color, radius float32, strokeWidth float32)
// DrawCircle fills a circle with the given color.
//
// The center and radius specify the circle geometry.
DrawCircle(center geometry.Point, radius float32, color Color)
// StrokeCircle draws the outline of a circle.
StrokeCircle(center geometry.Point, radius float32, color Color, strokeWidth float32)
// DrawLine draws a line between two points.
DrawLine(from, to geometry.Point, color Color, strokeWidth float32)
// DrawText draws text within the given bounding rectangle.
//
// The fontSize is in logical pixels. The bold flag selects bold weight.
// The align parameter controls horizontal alignment.
DrawText(text string, bounds geometry.Rect, fontSize float32, color Color, bold bool, align TextAlign)
// MeasureText returns the width in pixels of the given text string
// when rendered at the specified font size and weight.
// This is essential for accurate cursor positioning in text fields.
MeasureText(text string, fontSize float32, bold bool) float32
// DrawImage draws an image at the specified position.
//
// The image is drawn with its top-left corner at the given point.
// The image is composited using source-over blending. This method
// is used by RepaintBoundary to blit cached subtree renders.
DrawImage(img image.Image, at geometry.Point)
// PushClip pushes a clipping rectangle onto the clip stack.
//
// All subsequent drawing operations will be clipped to this rectangle
// intersected with any parent clip rectangles.
PushClip(r geometry.Rect)
// PushClipRoundRect pushes a rounded rectangle clipping region.
//
// All subsequent drawing operations will be clipped to this rounded
// rectangle. Uses GPU SDF-based clipping when available (gg.ClipRoundRect).
PushClipRoundRect(r geometry.Rect, radius float32)
// PopClip removes the most recently pushed clipping region.
//
// Must be called for each PushClip or PushClipRoundRect call.
PopClip()
// PushTransform pushes a translation transform onto the transform stack.
//
// All subsequent drawing operations will be offset by the given point.
PushTransform(offset geometry.Point)
// PopTransform removes the most recently pushed transform.
//
// Must be called for each PushTransform call.
PopTransform()
// TransformOffset returns the current cumulative transform offset.
//
// This is the total translation applied by all PushTransform calls
// currently on the transform stack. It represents the mapping from
// local widget coordinates to window (screen) coordinates.
//
// Used by [StampScreenOrigin] to compute a widget's screen-space
// position during the Draw pass.
TransformOffset() geometry.Point
}
Canvas provides drawing operations for widgets.
Canvas is passed to widgets during the Draw phase. It provides methods for drawing shapes, text, and images. The full implementation will be in the render package; this is a placeholder interface.
Coordinate System:
Canvas uses a coordinate system where (0,0) is the top-left corner of the window, X increases to the right, and Y increases downward. All coordinates are in logical pixels, which may be scaled for HiDPI displays.
Example:
func (w *MyWidget) Draw(ctx widget.Context, canvas widget.Canvas) {
// Fill background
canvas.DrawRect(w.Bounds(), widget.ColorWhite)
// Draw border
canvas.StrokeRect(w.Bounds(), widget.ColorBlack, 1.0)
}
Thread Safety:
Canvas is NOT thread-safe. All drawing operations must occur on the main/UI thread during the Draw phase.
type Color ¶
type Color struct {
R, G, B, A float32
}
Color represents an RGBA color with float32 components.
Each component is in the range [0, 1], where 0 is minimum intensity and 1 is maximum intensity. For alpha, 0 is fully transparent and 1 is fully opaque.
Color values use premultiplied alpha for efficient blending.
func Hex ¶
Hex creates a Color from a 24-bit hex value (0xRRGGBB).
Example:
skyBlue := widget.Hex(0x87CEEB) coral := widget.Hex(0xFF7F50)
func HexA ¶
HexA creates a Color from a 32-bit hex value with alpha (0xRRGGBBAA).
Example:
semiBlue := widget.HexA(0x0000FF80) // 50% transparent blue
func RGB ¶
RGB creates an opaque Color from red, green, and blue components.
All components should be in the range [0, 1].
Example:
white := widget.RGB(1, 1, 1) black := widget.RGB(0, 0, 0)
func RGB8 ¶
RGB8 creates an opaque Color from 8-bit RGB values (0-255).
Example:
white := widget.RGB8(255, 255, 255)
func RGBA ¶
RGBA creates a Color from red, green, blue, and alpha components.
All components should be in the range [0, 1].
Example:
red := widget.RGBA(1, 0, 0, 1) // Solid red semiRed := widget.RGBA(1, 0, 0, 0.5) // 50% transparent red
func RGBA8 ¶
RGBA8 creates a Color from 8-bit RGBA values (0-255).
Example:
red := widget.RGBA8(255, 0, 0, 255)
func (Color) IsTransparent ¶
IsTransparent returns true if the color is fully transparent (alpha == 0).
func (Color) Lerp ¶
Lerp returns a color linearly interpolated between c and other.
t=0 returns c, t=1 returns other.
Example:
// Fade from red to blue mid := widget.ColorRed.Lerp(widget.ColorBlue, 0.5)
type Context ¶
type Context interface {
// RequestFocus requests focus for the given widget.
//
// If another widget currently has focus, it will receive a focus lost event.
// The widget parameter should implement the Widget interface.
RequestFocus(w Widget)
// ReleaseFocus releases focus from the given widget.
//
// If the widget doesn't have focus, this is a no-op.
// After calling this, FocusedWidget() will return nil.
ReleaseFocus(w Widget)
// IsFocused returns true if the given widget currently has focus.
IsFocused(w Widget) bool
// FocusedWidget returns the currently focused widget, or nil if none.
FocusedWidget() Widget
// Now returns the current time.
//
// This is the time at the start of the current frame/event cycle.
// Use this for animations and time-based effects.
Now() time.Time
// DeltaTime returns the time elapsed since the previous frame.
//
// This is useful for smooth animations that should be frame-rate independent.
// Returns 0 for the first frame.
DeltaTime() time.Duration
// Invalidate marks the entire window as needing a redraw.
//
// Call this when widget state changes require visual updates.
// Multiple calls per frame are coalesced into a single redraw.
Invalidate()
// InvalidateRect marks a specific rectangular area as needing a redraw.
//
// Use this for more efficient partial redraws when only a small
// part of the UI has changed.
InvalidateRect(r geometry.Rect)
// Cursor returns the current cursor type.
Cursor() CursorType
// SetCursor changes the mouse cursor.
//
// The cursor is typically reset to CursorDefault at the start of each frame.
SetCursor(cursor CursorType)
// Scale returns the display scale factor (DPI scaling).
//
// Returns 1.0 for standard displays, 2.0 for Retina/HiDPI displays, etc.
// Use this to scale coordinates and sizes for proper rendering.
Scale() float32
// ThemeProvider returns the current theme for this context.
//
// Returns nil if no theme is set (headless mode without a theme).
// Widgets should check for nil before using the returned provider.
ThemeProvider() ThemeProvider
// OverlayManager returns the overlay manager for pushing/removing overlays.
//
// Returns nil if no overlay manager is set (headless mode without a window).
// Widgets should check for nil before calling overlay methods.
OverlayManager() OverlayManager
// WindowSize returns the current window size in logical pixels.
WindowSize() geometry.Size
// Scheduler returns the signal scheduler for this context.
//
// Returns nil if no scheduler is set (headless mode without signal support).
// Widgets should check for nil before using the returned scheduler.
Scheduler() SchedulerRef
}
Context provides access to UI state during layout, drawing, and event handling.
Context is passed through the widget tree during all phases (Layout, Draw, Event). It provides:
- Focus management: Request/release focus, query focused widget
- Time information: Current time and delta for animations
- Invalidation: Mark areas as needing redraw
- Cursor management: Change the mouse cursor
- Theme access: Query the current visual theme
Thread Safety:
Context implementations must be safe for concurrent access. The default implementation ContextImpl uses a mutex to protect internal state.
Example:
func (w *MyWidget) Event(ctx widget.Context, e event.Event) bool {
if clicked {
ctx.RequestFocus(w)
ctx.Invalidate()
return true
}
return false
}
type ContextImpl ¶
type ContextImpl struct {
// contains filtered or unexported fields
}
ContextImpl is the standard implementation of the Context interface.
It provides thread-safe focus management, time tracking, and invalidation. Create a new ContextImpl with NewContext.
Example:
ctx := widget.NewContext() ctx.SetNow(time.Now()) // Pass to widget tree during layout/draw/event
func NewContext ¶
func NewContext() *ContextImpl
NewContext creates a new ContextImpl with default settings.
The context is initialized with:
- No focused widget
- Current time set to time.Now()
- Scale factor of 1.0
- Default cursor
func (*ContextImpl) ClearInvalidation ¶
func (c *ContextImpl) ClearInvalidation()
ClearInvalidation clears the invalidation state.
Call this after processing a redraw.
func (*ContextImpl) Cursor ¶
func (c *ContextImpl) Cursor() CursorType
Cursor returns the current cursor type.
func (*ContextImpl) DeltaTime ¶
func (c *ContextImpl) DeltaTime() time.Duration
DeltaTime returns the time elapsed since the previous frame.
func (*ContextImpl) FocusedWidget ¶
func (c *ContextImpl) FocusedWidget() Widget
FocusedWidget returns the currently focused widget, or nil if none.
func (*ContextImpl) Invalidate ¶
func (c *ContextImpl) Invalidate()
Invalidate marks the entire window as needing a redraw.
func (*ContextImpl) InvalidateRect ¶
func (c *ContextImpl) InvalidateRect(r geometry.Rect)
InvalidateRect marks a specific rectangular area as needing a redraw.
func (*ContextImpl) InvalidatedRect ¶
func (c *ContextImpl) InvalidatedRect() geometry.Rect
InvalidatedRect returns the area that needs redrawing.
Returns an empty rect if no partial invalidation was requested, or if a full invalidation was requested (check IsInvalidated).
func (*ContextImpl) IsFocused ¶
func (c *ContextImpl) IsFocused(w Widget) bool
IsFocused returns true if the given widget currently has focus.
func (*ContextImpl) IsInvalidated ¶
func (c *ContextImpl) IsInvalidated() bool
IsInvalidated returns true if the window needs a redraw.
func (*ContextImpl) OverlayManager ¶
func (c *ContextImpl) OverlayManager() OverlayManager
OverlayManager returns the overlay manager, or nil if none is set.
func (*ContextImpl) ReleaseFocus ¶
func (c *ContextImpl) ReleaseFocus(w Widget)
ReleaseFocus releases focus from the given widget.
func (*ContextImpl) RequestFocus ¶
func (c *ContextImpl) RequestFocus(w Widget)
RequestFocus requests focus for the given widget.
func (*ContextImpl) ResetCursor ¶
func (c *ContextImpl) ResetCursor()
ResetCursor resets the cursor to default.
Call this at the start of each frame before processing events.
func (*ContextImpl) Scale ¶
func (c *ContextImpl) Scale() float32
Scale returns the display scale factor.
func (*ContextImpl) Scheduler ¶
func (c *ContextImpl) Scheduler() SchedulerRef
Scheduler returns the signal scheduler for this context.
Returns nil if no scheduler is set.
func (*ContextImpl) SetCursor ¶
func (c *ContextImpl) SetCursor(cursor CursorType)
SetCursor changes the mouse cursor.
func (*ContextImpl) SetNow ¶
func (c *ContextImpl) SetNow(now time.Time)
SetNow updates the current time and calculates delta time.
Call this at the start of each frame before processing events and layout.
func (*ContextImpl) SetOnInvalidate ¶
func (c *ContextImpl) SetOnInvalidate(callback func())
SetOnInvalidate sets a callback function called when Invalidate is called.
func (*ContextImpl) SetOnInvalidateRect ¶
func (c *ContextImpl) SetOnInvalidateRect(callback func(geometry.Rect))
SetOnInvalidateRect sets a callback function called when InvalidateRect is called.
func (*ContextImpl) SetOverlayManager ¶
func (c *ContextImpl) SetOverlayManager(om OverlayManager)
SetOverlayManager sets the overlay manager for this context.
func (*ContextImpl) SetScale ¶
func (c *ContextImpl) SetScale(scale float32)
SetScale sets the display scale factor.
func (*ContextImpl) SetScheduler ¶
func (c *ContextImpl) SetScheduler(s SchedulerRef)
SetScheduler sets the signal scheduler for this context.
func (*ContextImpl) SetThemeProvider ¶
func (c *ContextImpl) SetThemeProvider(tp ThemeProvider)
SetThemeProvider sets the theme provider for this context.
Pass nil to clear the theme provider (e.g., for headless testing).
func (*ContextImpl) SetWindowSize ¶
func (c *ContextImpl) SetWindowSize(size geometry.Size)
SetWindowSize sets the current window size.
func (*ContextImpl) ThemeProvider ¶
func (c *ContextImpl) ThemeProvider() ThemeProvider
ThemeProvider returns the current theme for this context.
Returns nil if no theme is set (headless mode without a theme).
func (*ContextImpl) WindowSize ¶
func (c *ContextImpl) WindowSize() geometry.Size
WindowSize returns the current window size in logical pixels.
type CursorType ¶
type CursorType uint8
CursorType represents the type of mouse cursor to display.
const ( // CursorDefault is the standard arrow cursor. CursorDefault CursorType = iota // CursorPointer is the pointing hand cursor, typically for links. CursorPointer // CursorText is the I-beam cursor for text selection. CursorText // CursorCrosshair is the crosshair cursor for precise selection. CursorCrosshair // CursorMove is the four-arrow move cursor. CursorMove // CursorResizeNS is the north-south (vertical) resize cursor. CursorResizeNS // CursorResizeEW is the east-west (horizontal) resize cursor. CursorResizeEW // CursorResizeNESW is the diagonal (northeast-southwest) resize cursor. CursorResizeNESW // CursorResizeNWSE is the diagonal (northwest-southeast) resize cursor. CursorResizeNWSE // CursorNotAllowed is the circle with a line through it (forbidden) cursor. CursorNotAllowed // CursorWait is the wait/busy cursor (hourglass or spinner). CursorWait // CursorNone hides the cursor. CursorNone )
Cursor type constants.
func (CursorType) String ¶
func (c CursorType) String() string
String returns a human-readable name for the cursor type.
type DrawStats ¶
type DrawStats struct {
// TotalWidgets is the total number of widgets visited during traversal.
TotalWidgets int
// DrawnWidgets is the number of widgets that had their Draw called.
DrawnWidgets int
// SkippedWidgets is the number of widgets skipped (invisible or nil).
SkippedWidgets int
// DirtyWidgets is the number of widgets that had their needsRedraw flag set.
// In Sub-Phase 1 this equals DrawnWidgets for visible widgets.
// In Sub-Phase 2 with pixel caching, clean widgets will be composited
// from cache instead of re-drawn, so DirtyWidgets < DrawnWidgets.
DirtyWidgets int
// CleanWidgets is the number of visible widgets that did NOT have
// their needsRedraw flag set. These are candidates for pixel caching
// in Sub-Phase 2.
CleanWidgets int
// CachedWidgets is the number of RepaintBoundary widgets that served
// their content from a cached pixmap instead of re-rendering the
// child subtree. This is the primary metric for Sub-Phase 2 pixel
// caching effectiveness.
CachedWidgets int
}
DrawStats holds statistics from a draw tree traversal.
These statistics provide observability into the retained-mode rendering system. They track how many widgets were drawn versus skipped, enabling performance monitoring and validation that the dirty-tracking system is working correctly.
In Sub-Phase 1 (frame-level skip), all widgets in a dirty frame are drawn because gg clears the pixmap each frame. The stats still track which widgets WERE dirty vs clean, providing the foundation for Sub-Phase 2 (RepaintBoundary) where clean subtrees will be composited from cached textures instead of re-drawn.
func CollectDrawStats ¶
CollectDrawStats walks the widget tree and counts dirty/clean widgets WITHOUT drawing anything. This is useful for diagnostics and testing.
Unlike DrawTree, this function does not call Draw and does not clear any redraw flags.
func DrawTree ¶
DrawTree performs a draw traversal of the widget tree rooted at w, collecting statistics about dirty/clean widget state.
All visible widgets are drawn regardless of their dirty state because the current rendering backend (gg) clears the pixmap each frame. The statistics track which widgets were dirty vs clean, providing observability and the foundation for future pixel-caching optimizations.
DrawTree clears the needsRedraw flag on each widget after drawing it, so subsequent frames will see the tree as clean unless new signal changes mark widgets dirty again.
If w is nil, DrawTree returns zero stats and does nothing.
type Focusable ¶
type Focusable interface {
// IsFocusable reports whether this widget can currently receive focus.
//
// A widget may return false if it is disabled, invisible, or otherwise
// unable to accept keyboard input at this time.
IsFocusable() bool
// SetFocused sets the widget's focus state.
//
// The focus manager calls this when focus is granted or revoked.
// Widgets should update their visual appearance accordingly.
SetFocused(focused bool)
// IsFocused reports whether this widget currently has keyboard focus.
IsFocused() bool
}
Focusable is implemented by widgets that can receive keyboard focus.
Widgets that support keyboard interaction (text inputs, buttons, etc.) should implement this interface in addition to the Widget interface. The focus manager uses this interface to determine which widgets participate in tab navigation.
WidgetBase already implements SetFocused and IsFocused, so concrete widgets only need to implement IsFocusable to opt into focus management.
Example:
type TextInput struct {
widget.WidgetBase
}
func (t *TextInput) IsFocusable() bool {
return t.IsEnabled() && t.IsVisible()
}
type LayoutFunc ¶
type LayoutFunc func(ctx Context, constraints geometry.Constraints) geometry.Size
LayoutFunc is a function type for custom layout logic.
This can be used to implement layout behavior without creating a full widget implementation.
type Lifecycle ¶
type Lifecycle interface {
// Mount is called when the widget is added to the tree.
// Implementations should create signal bindings here via AddBinding().
Mount(ctx Context)
// Unmount is called when the widget is removed from the tree.
// Implementations should clean up any resources not managed by AddBinding().
// WidgetBase.CleanupBindings() is called automatically before Unmount().
Unmount()
}
Lifecycle is an optional interface that widgets implement to receive mount/unmount notifications from the widget tree.
When a widget with signal bindings is added to the tree, Mount is called to create subscriptions. When removed, Unmount is called to clean them up.
Widgets that do not use signals need not implement this interface. The framework checks for Lifecycle via type assertion.
type OverlayManager ¶
type OverlayManager interface {
// PushOverlay pushes a widget as an overlay. The onDismiss callback is
// called when the overlay should be closed (e.g. click outside, Escape key).
PushOverlay(w Widget, onDismiss func())
// PopOverlay removes the topmost overlay from the stack.
PopOverlay()
// RemoveOverlay removes a specific overlay widget from the stack.
RemoveOverlay(w Widget)
}
OverlayManager provides methods for pushing and removing overlays from the window's overlay stack. This interface lives in the widget package to avoid circular imports: the overlay package imports widget, so widget cannot import overlay. Instead, widgets call OverlayManager methods on the Context without needing to know the concrete overlay.Stack type.
type SchedulerRef ¶
type SchedulerRef interface {
MarkDirty(w Widget)
}
SchedulerRef is a minimal interface for the signal scheduler. It is defined in the widget package to avoid circular imports between widget and state packages.
type Stopper ¶
type Stopper interface {
Stop()
}
Stopper is implemented by effects for cleanup. It is defined here to avoid importing the state package from widget.
type TextAlign ¶
type TextAlign uint8
TextAlign specifies horizontal text alignment within bounds.
type ThemeProvider ¶
type ThemeProvider interface {
// IsDark returns true if this is a dark theme.
IsDark() bool
// OnSurface returns the default color for text and icons on surface
// backgrounds. Every design system (Material 3, Fluent, Cupertino)
// defines an equivalent concept, making this a universal token.
OnSurface() Color
}
ThemeProvider gives widgets access to the current visual theme.
Concrete theme types (theme.Theme, material3.Theme) implement this interface. The widget package defines only the interface to avoid import cycles between widget and theme packages.
Widgets should use ThemeProvider for visual decisions (e.g., choosing colors based on dark/light mode, default text color) rather than importing a concrete theme package directly.
type Unbinder ¶
type Unbinder interface {
Unbind()
}
Unbinder is implemented by signal bindings for cleanup. It is defined here to avoid importing the state package from widget.
type Widget ¶
type Widget interface {
// Layout calculates the widget's size given constraints from the parent.
//
// The constraints define the minimum and maximum allowed dimensions.
// The returned size must satisfy the constraints (within min/max bounds).
//
// Layout is called during the layout phase, before Draw. The widget
// should calculate its preferred size and return it constrained to
// the allowed range.
//
// For container widgets, Layout should:
// 1. Layout all children with appropriate constraints
// 2. Position children by setting their bounds
// 3. Return the container's total size
//
// Example:
//
// func (w *MyWidget) Layout(ctx Context, c geometry.Constraints) geometry.Size {
// preferred := geometry.Sz(100, 50)
// return c.Constrain(preferred)
// }
Layout(ctx Context, constraints geometry.Constraints) geometry.Size
// Draw renders the widget to the canvas.
//
// Draw is called after Layout, when the widget's bounds are established.
// The canvas provides drawing operations for rendering the widget.
//
// For container widgets, Draw should:
// 1. Draw the container's own content (background, border, etc.)
// 2. Draw all visible children
//
// Example:
//
// func (w *MyWidget) Draw(ctx Context, canvas Canvas) {
// canvas.DrawRect(w.Bounds(), w.backgroundColor)
// for _, child := range w.Children() {
// child.Draw(ctx, canvas)
// }
// }
Draw(ctx Context, canvas Canvas)
// Event handles an input event and returns true if consumed.
//
// Events are dispatched from the root widget down through the tree.
// A widget that handles an event should return true to prevent
// further propagation.
//
// For container widgets, Event should:
// 1. Check if event is within bounds
// 2. Dispatch to appropriate child widgets
// 3. Handle the event if no child consumed it
//
// Example:
//
// func (w *MyWidget) Event(ctx Context, e event.Event) bool {
// if me, ok := e.(*event.MouseEvent); ok {
// if !w.Bounds().Contains(me.Position) {
// return false
// }
// if me.MouseType == event.MousePress {
// w.onClick()
// return true
// }
// }
// return false
// }
Event(ctx Context, e event.Event) bool
// Children returns the widget's child widgets.
//
// Leaf widgets (like labels, buttons) return nil.
// Container widgets return their children in z-order (bottom to top).
//
// The returned slice should not be modified by the caller.
Children() []Widget
}
Widget is the fundamental building block of the UI framework.
All UI elements implement this interface to participate in layout, drawing, and event handling. Widgets form a tree structure where parent widgets contain and manage child widgets.
The widget lifecycle consists of three phases:
- Layout: Calculate size given constraints from parent
- Draw: Render the widget to a canvas
- Event: Handle user input events
Implementations should embed WidgetBase to get common functionality like bounds tracking, visibility, and enabled state management.
type WidgetBase ¶
type WidgetBase struct {
// contains filtered or unexported fields
}
WidgetBase provides common functionality for widgets.
Embed this struct in custom widget implementations to get:
- Bounds tracking (position and size)
- Screen-space coordinate tracking
- Focus state management
- Visibility control
- Enabled/disabled state
- Child widget management
- Optional ID for debugging
- Signal binding lifecycle management
- Retained-mode redraw tracking
Example usage:
type MyButton struct {
widget.WidgetBase
label string
}
func NewMyButton(label string) *MyButton {
b := &MyButton{label: label}
b.SetVisible(true)
b.SetEnabled(true)
return b
}
Thread Safety:
WidgetBase uses a mutex to protect its internal state. However, this does not make widgets thread-safe for general use. All widget operations should occur on the main/UI thread. The mutex is provided for cases where properties need to be queried from callbacks.
func NewWidgetBase ¶
func NewWidgetBase() *WidgetBase
NewWidgetBase creates a new WidgetBase with default settings.
The widget is visible and enabled by default, with no children and zero bounds.
func (*WidgetBase) AddBinding ¶
func (w *WidgetBase) AddBinding(b Unbinder)
AddBinding registers a signal binding for automatic cleanup on unmount.
func (*WidgetBase) AddChild ¶
func (w *WidgetBase) AddChild(child Widget)
AddChild adds a child widget.
If the child has a WidgetBase that can be accessed, its parent is set to this widget.
func (*WidgetBase) AddEffect ¶
func (w *WidgetBase) AddEffect(e Stopper)
AddEffect registers an effect for automatic cleanup on unmount.
func (*WidgetBase) Bounds ¶
func (w *WidgetBase) Bounds() geometry.Rect
Bounds returns the widget's current bounds (position and size).
The bounds are set during layout by the parent widget.
func (*WidgetBase) ChildAt ¶
func (w *WidgetBase) ChildAt(index int) Widget
ChildAt returns the child at the given index, or nil if out of range.
func (*WidgetBase) ChildCount ¶
func (w *WidgetBase) ChildCount() int
ChildCount returns the number of child widgets.
func (*WidgetBase) Children ¶
func (w *WidgetBase) Children() []Widget
Children returns the widget's child widgets.
Returns nil for leaf widgets with no children. The returned slice should not be modified by the caller.
func (*WidgetBase) CleanupBindings ¶
func (w *WidgetBase) CleanupBindings()
CleanupBindings unbinds all signal bindings and stops all effects. Called automatically by the framework before Unmount().
func (*WidgetBase) ClearChildren ¶
func (w *WidgetBase) ClearChildren()
ClearChildren removes all child widgets.
func (*WidgetBase) ClearRedraw ¶
func (w *WidgetBase) ClearRedraw()
ClearRedraw clears the widget's needsRedraw flag after a successful draw.
This should be called by the rendering system after the widget has been drawn, to indicate that its visual state is now up to date.
func (*WidgetBase) ContainsPoint ¶
func (w *WidgetBase) ContainsPoint(p geometry.Point) bool
ContainsPoint returns true if the point is within the widget's bounds.
This is a convenience method for hit testing.
func (*WidgetBase) GlobalToLocal ¶
func (w *WidgetBase) GlobalToLocal(p geometry.Point) geometry.Point
GlobalToLocal converts a point from global (window) coordinates to local coordinates.
Local coordinates are relative to the widget's top-left corner. Global coordinates are relative to the window's top-left corner.
This method uses the screen origin computed during the Draw pass, which accounts for all parent transforms including scroll offsets.
func (*WidgetBase) HasChildren ¶
func (w *WidgetBase) HasChildren() bool
HasChildren returns true if the widget has any children.
func (*WidgetBase) ID ¶
func (w *WidgetBase) ID() string
ID returns the widget's ID for debugging purposes.
IDs are optional and not used by the framework itself. They are useful for debugging and testing.
func (*WidgetBase) InsertChild ¶
func (w *WidgetBase) InsertChild(index int, child Widget)
InsertChild inserts a child widget at the given index.
If index is out of range, the child is appended.
func (*WidgetBase) IsEnabled ¶
func (w *WidgetBase) IsEnabled() bool
IsEnabled returns true if the widget accepts input.
Disabled widgets are drawn (usually with a dimmed appearance) but do not respond to user input.
func (*WidgetBase) IsFocused ¶
func (w *WidgetBase) IsFocused() bool
IsFocused returns true if the widget currently has focus.
func (*WidgetBase) IsMounted ¶
func (w *WidgetBase) IsMounted() bool
IsMounted reports whether the widget is currently in the mounted tree.
func (*WidgetBase) IsVisible ¶
func (w *WidgetBase) IsVisible() bool
IsVisible returns true if the widget is visible.
Invisible widgets are not drawn and do not receive events.
func (*WidgetBase) LocalToGlobal ¶
func (w *WidgetBase) LocalToGlobal(p geometry.Point) geometry.Point
LocalToGlobal converts a point from local coordinates to global (window) coordinates.
Local coordinates are relative to the widget's top-left corner. Global coordinates are relative to the window's top-left corner.
This method uses the screen origin computed during the Draw pass, which accounts for all parent transforms including scroll offsets.
func (*WidgetBase) NeedsRedraw ¶
func (w *WidgetBase) NeedsRedraw() bool
NeedsRedraw reports whether the widget needs re-rendering.
This flag is set by the signal scheduler when a bound signal changes, and cleared after the widget is drawn. It persists across scheduler flushes, surviving until the actual draw pass processes it.
This is part of the retained-mode rendering system: widgets marked as needing redraw will trigger a full draw pass, while a tree with no dirty widgets allows the frame to skip rendering entirely.
func (*WidgetBase) Parent ¶
func (w *WidgetBase) Parent() Widget
Parent returns the widget's parent, or nil if none.
func (*WidgetBase) Position ¶
func (w *WidgetBase) Position() geometry.Point
Position returns the widget's top-left position.
func (*WidgetBase) RemoveChild ¶
func (w *WidgetBase) RemoveChild(child Widget) bool
RemoveChild removes a child widget.
Returns true if the child was found and removed.
func (*WidgetBase) RemoveChildAt ¶
func (w *WidgetBase) RemoveChildAt(index int) Widget
RemoveChildAt removes the child at the given index.
Returns the removed child, or nil if the index is out of range.
func (*WidgetBase) ScreenBounds ¶
func (w *WidgetBase) ScreenBounds() geometry.Rect
ScreenBounds returns the widget's bounds in window (screen) coordinates.
Screen bounds are computed during the Draw pass by the framework, reflecting all accumulated transforms from parent containers (scroll offsets, box positions, etc.). This is the correct value to use when positioning overlays, popups, tooltips, and context menus.
Before the first Draw pass completes, this returns a rect at (0,0) with the widget's size.
Example:
func (w *MyWidget) showPopup(ctx widget.Context) {
anchor := w.ScreenBounds()
pos := overlay.Position(overlay.PlacementBelow, anchor, popupSize, windowSize, 4)
// pos is now correct even if w is inside a ScrollView
}
func (*WidgetBase) ScreenOrigin ¶
func (w *WidgetBase) ScreenOrigin() geometry.Point
ScreenOrigin returns the widget's top-left corner in window (screen) coordinates.
This value is computed during the Draw pass by the framework via StampScreenOrigin, reflecting all accumulated transforms from parent containers (scroll offsets, box positions, etc.).
Before the first Draw pass completes, this returns the zero point.
func (*WidgetBase) SetBounds ¶
func (w *WidgetBase) SetBounds(bounds geometry.Rect)
SetBounds sets the widget's bounds.
This is typically called by the parent widget during layout after the child's Layout() method returns its size.
func (*WidgetBase) SetEnabled ¶
func (w *WidgetBase) SetEnabled(enabled bool)
SetEnabled sets whether the widget accepts input.
func (*WidgetBase) SetFocused ¶
func (w *WidgetBase) SetFocused(focused bool)
SetFocused sets the widget's focus state.
Note: To properly manage focus in the UI, use Context.RequestFocus() and Context.ReleaseFocus() instead of calling this directly.
func (*WidgetBase) SetID ¶
func (w *WidgetBase) SetID(id string)
SetID sets the widget's ID for debugging purposes.
func (*WidgetBase) SetMounted ¶
func (w *WidgetBase) SetMounted(m bool)
SetMounted sets the widget's mounted state. This is called by the framework during mount/unmount tree walks.
func (*WidgetBase) SetNeedsRedraw ¶
func (w *WidgetBase) SetNeedsRedraw(v bool)
SetNeedsRedraw marks the widget as needing re-rendering.
This is called by the signal scheduler's flush callback when a widget's bound signal has changed. Unlike the scheduler's pending set (which is cleared on flush), this flag persists until the draw pass clears it via WidgetBase.ClearRedraw.
func (*WidgetBase) SetParent ¶
func (w *WidgetBase) SetParent(parent Widget)
SetParent sets the widget's parent.
This is called automatically by AddChild and RemoveChild.
func (*WidgetBase) SetScreenOrigin ¶
func (w *WidgetBase) SetScreenOrigin(origin geometry.Point)
SetScreenOrigin records the widget's window-space origin.
This is called by the framework during the Draw pass via StampScreenOrigin. User code should not call this method directly.
func (*WidgetBase) SetVisible ¶
func (w *WidgetBase) SetVisible(visible bool)
SetVisible sets the widget's visibility.
func (*WidgetBase) Size ¶
func (w *WidgetBase) Size() geometry.Size
Size returns the widget's current size.