widget

package
v0.1.28 Latest Latest
Warning

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

Go to latest
Published: May 16, 2026 License: MIT Imports: 9 Imported by: 0

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:

  1. Parent passes constraints to child via Layout()
  2. Child calculates its size within those constraints
  3. Child returns its chosen size to parent
  4. 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

Constants

This section is empty.

Variables

View Source
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 DrawChild added in v0.1.19

func DrawChild(child Widget, ctx Context, canvas Canvas)

DrawChild draws a child widget with RepaintBoundary support.

Container widgets (BoxWidget, VBox, ListView) should call this instead of child.Draw() directly. If the child has IsRepaintBoundary=true (ADR-024 WidgetBase property), drawing is routed through scene caching via drawBoundaryWidget. Otherwise, child.Draw() is called directly.

This is the Flutter PaintingContext.paintChild pattern: the parent checks child.isRepaintBoundary before painting.

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

func MountTree(w Widget, ctx Context)

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

func NeedsRedrawInTree(w Widget) bool

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 NeedsRedrawInTreeNonBoundary added in v0.1.19

func NeedsRedrawInTreeNonBoundary(w Widget) bool

NeedsRedrawInTreeNonBoundary reports whether any NON-BOUNDARY widget in the subtree needs re-rendering. RepaintBoundary widgets are skipped because they manage their own dirty state independently. This prevents offscreen animated boundaries (spinner scrolled out of view) from forcing expensive root re-recording on every frame.

func RegisterSceneRecorder added in v0.1.19

func RegisterSceneRecorder(factory SceneRecorder)

RegisterSceneRecorder registers the factory function for creating scene recording canvases. This must be called by the app layer before any boundary draws occur (typically in package init or Window creation).

Example (from app package):

widget.RegisterSceneRecorder(func(s *scene.Scene, w, h int) (widget.Canvas, func()) {
    recorder := render.NewSceneCanvas(s, w, h)
    return recorder, recorder.Close
})

func StampScreenOrigin

func StampScreenOrigin(child Widget, canvas Canvas)

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 AnimationScheduler added in v0.1.19

type AnimationScheduler interface {
	ScheduleAnimationFrame()
}

AnimationScheduler is an optional interface for deferred animation frame requests. Animated widgets (spinners, progress bars) use this instead of ctx.InvalidateRect() to avoid triggering immediate RequestRedraw.

The framework's animation pumper controls the actual frame rate — animated widgets just request "paint me on the next animation tick", not "paint me RIGHT NOW".

Flutter equivalent: SchedulerBinding.scheduleFrame() — defers to next vsync, does NOT trigger immediate render. Multiple calls coalesce. Qt equivalent: QTimer-driven update() — deferred to event loop. Android equivalent: Choreographer.postFrameCallback() — next vsync.

Usage in animated widgets:

if sched, ok := ctx.(widget.AnimationScheduler); ok {
    sched.ScheduleAnimationFrame()
} else {
    ctx.InvalidateRect(w.Bounds()) // fallback: immediate
}

type ArcStroker added in v0.1.14

type ArcStroker interface {
	// StrokeArcStyled draws a circular arc with the specified line cap style.
	StrokeArcStyled(center geometry.Point, radius float32, startAngle, sweepAngle float64,
		color Color, strokeWidth float32, lineCap LineCap)
}

ArcStroker is an optional interface for canvases that support styled arc strokes. Use type assertion to check: if s, ok := canvas.(ArcStroker); ok { ... }

type BoundaryRecorder added in v0.1.19

type BoundaryRecorder interface {
	IsBoundaryRecording() bool
}

BoundaryRecorder is implemented by canvases that record into a boundary's scene.Scene. When DrawChild encounters a child that IS a boundary, it skips drawing — the child boundary has its own PictureLayer in the compositor.

Flutter equivalent: PaintingContext knows it's recording into a boundary's PictureRecorder. paintChild checks isRepaintBoundary → appendLayer instead of painting into the current recorder.

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)

	// FillRectDirect fills a rectangle using CPU-only rendering, bypassing
	// the GPU shape accelerator. Used for dirty-region background clearing
	// where GPU acceleration is counterproductive (it blocks the compositor
	// non-MSAA blit-only fast path, causing 10x GPU overhead).
	FillRectDirect(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)

	// StrokeArc draws a circular arc outline from startAngle with the given sweep.
	// Angles are in radians. startAngle=0 is 3 o'clock, positive is counterclockwise.
	// The arc is rendered as a single stroke operation using cubic Bézier curves.
	StrokeArc(center geometry.Point, radius float32, startAngle, sweepAngle float64,
		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.
	TransformOffset() geometry.Point

	// ScreenOriginBase returns the screen-space base offset for this canvas.
	//
	// For the main window canvas this is (0,0). For SceneCanvas inside
	// RepaintBoundary recording, this is the boundary widget's screen position.
	// Without this, PushTransform(-bounds.Min) for local coords makes
	// TransformOffset() return local values, and StampScreenOrigin computes
	// wrong ScreenOrigin → dirty.Collector reports regions at (0,0).
	//
	// Flutter equivalent: PaintingContext.offset in RenderObject.paint().
	ScreenOriginBase() geometry.Point

	// ClipBounds returns the current clip rectangle.
	//
	// This is the intersection of all PushClip/PushClipRoundRect regions
	// currently on the clip stack. Used for viewport culling: containers
	// skip Draw on children whose bounds don't intersect the clip region,
	// preventing offscreen widgets from ticking animations.
	ClipBounds() geometry.Rect

	// ReplayScene renders a previously recorded scene.Scene display list
	// into this canvas. Used by RepaintBoundary to replay cached content
	// without re-executing the child widget's Draw method.
	//
	// For Canvas (gg.Context wrapper): renders the scene via GPU scene
	// renderer, which routes commands through gg.Context's GPU accelerator.
	// For SceneCanvas: merges the child scene into the parent scene via
	// Scene.Append (O(commands), zero re-encoding).
	//
	// If s is nil or empty, this is a no-op.
	ReplayScene(s *scene.Scene)
}

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

func Hex(hex uint32) Color

Hex creates a Color from a 24-bit hex value (0xRRGGBB).

Example:

skyBlue := widget.Hex(0x87CEEB)
coral := widget.Hex(0xFF7F50)

func HexA

func HexA(hex uint32) Color

HexA creates a Color from a 32-bit hex value with alpha (0xRRGGBBAA).

Example:

semiBlue := widget.HexA(0x0000FF80) // 50% transparent blue

func RGB

func RGB(r, g, b float32) Color

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

func RGB8(r, g, b uint8) Color

RGB8 creates an opaque Color from 8-bit RGB values (0-255).

Example:

white := widget.RGB8(255, 255, 255)

func RGBA

func RGBA(r, g, b, a float32) Color

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

func RGBA8(r, g, b, a uint8) Color

RGBA8 creates a Color from 8-bit RGBA values (0-255).

Example:

red := widget.RGBA8(255, 0, 0, 255)

func (Color) IsOpaque

func (c Color) IsOpaque() bool

IsOpaque returns true if the color is fully opaque (alpha == 1).

func (Color) IsTransparent

func (c Color) IsTransparent() bool

IsTransparent returns true if the color is fully transparent (alpha == 0).

func (Color) Lerp

func (c Color) Lerp(other Color, t float32) Color

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)

func (Color) RGBA8

func (c Color) RGBA8() (r, g, b, a uint8)

RGBA8 returns the color as 8-bit RGBA components (0-255).

func (Color) WithAlpha

func (c Color) WithAlpha(a float32) Color

WithAlpha returns a copy of the color with the given alpha value.

Example:

semiRed := widget.ColorRed.WithAlpha(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) BeginFrame added in v0.1.3

func (c *ContextImpl) BeginFrame(now time.Time)

BeginFrame updates the frame timing. DeltaTime is calculated from the previous BeginFrame call, not from the last SetNow. This ensures DeltaTime always reflects the inter-frame interval regardless of how many HandleEvent calls happen between frames. See issue #53.

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) DirtyTracker added in v0.1.14

func (c *ContextImpl) DirtyTracker() DirtyTrackerRef

DirtyTracker returns the dirty region tracker for the current draw pass.

Returns nil when no draw pass is in progress or when the tracker was not set (e.g., during full repaints where all widgets are drawn regardless). During an incremental draw pass, the caller sets a DirtyTrackerRef via [SetDirtyTracker] so that widgets like RepaintBoundary can query spatial dirty regions for O(regions) early exit.

This method is on the concrete ContextImpl type (not the Context interface) because adding methods to the interface would be a breaking change for all implementors.

func (*ContextImpl) DrawStats added in v0.1.14

func (c *ContextImpl) DrawStats() *DrawStats

DrawStats returns the current draw statistics collector for this frame.

Returns nil when no draw pass is in progress. During a draw pass (DrawTree or drawWidgetsInRegion), the caller sets a *DrawStats via [SetDrawStats] so that widgets like RepaintBoundary can record cache hits.

This method is on the concrete ContextImpl type (not the Context interface) because adding methods to the interface would be a breaking change for all implementors.

func (*ContextImpl) FocusedWidget

func (c *ContextImpl) FocusedWidget() Widget

FocusedWidget returns the currently focused widget, or nil if none.

func (*ContextImpl) ImageCache added in v0.1.14

func (c *ContextImpl) ImageCache() ImageCacheRef

ImageCache returns the centralized RepaintBoundary pixel cache.

Returns nil when no cache is configured (e.g., headless testing without a Window). When a Window owns an ImageCache, it calls [SetImageCache] during initialization so that all RepaintBoundary instances share a single LRU cache with memory budget enforcement.

This method is on the concrete ContextImpl type (not the Context interface) because adding methods to the interface would be a breaking change for all implementors.

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) Now

func (c *ContextImpl) Now() time.Time

Now returns the current time.

func (*ContextImpl) OverlayManager

func (c *ContextImpl) OverlayManager() OverlayManager

OverlayManager returns the overlay manager, or nil if none is set.

func (*ContextImpl) RegisterDirtyBoundary added in v0.1.20

func (c *ContextImpl) RegisterDirtyBoundary(key uint64)

RegisterDirtyBoundary registers a RepaintBoundary as dirty in the Window's flat dirty boundary set. Called from the onBoundaryDirty callback wired by PaintBoundaryLayers.

This populates the O(1) dirty boundary map that replaces O(n) NeedsRedrawInTreeNonBoundary tree walks for frame skip decisions. The key is the boundary's unique BoundaryCacheKey for deduplication.

If no callback is wired (headless tests), this is a no-op.

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) ScheduleAnimationFrame added in v0.1.19

func (c *ContextImpl) ScheduleAnimationFrame()

ScheduleAnimationFrame requests that the render loop stay active for animation. Unlike InvalidateRect, this does NOT trigger an immediate RequestRedraw — it ensures the animation pumper keeps ticking at its configured rate (default 30fps). The next pump tick will render any dirty boundaries.

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) SetDirtyTracker added in v0.1.14

func (c *ContextImpl) SetDirtyTracker(tracker DirtyTrackerRef)

SetDirtyTracker sets the dirty region tracker for the current draw pass.

Pass a non-nil DirtyTrackerRef before starting an incremental draw pass so that widgets can query spatial dirty regions. Pass nil after the draw pass to release the reference.

func (*ContextImpl) SetDrawStats added in v0.1.14

func (c *ContextImpl) SetDrawStats(stats *DrawStats)

SetDrawStats sets the draw statistics collector for the current frame.

Pass a non-nil *DrawStats before starting a draw pass so that widgets can record metrics. Pass nil after the draw pass to release the reference.

func (*ContextImpl) SetImageCache added in v0.1.14

func (c *ContextImpl) SetImageCache(cache ImageCacheRef)

SetImageCache sets the centralized RepaintBoundary pixel cache.

Pass a non-nil ImageCacheRef during Window initialization to enable shared caching. Pass nil to disable (RepaintBoundary falls back to per-widget local cache).

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. DeltaTime is clamped to [0, 100ms] to prevent animation jumps after long pauses (e.g., background/resume, debugger breakpoints).

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) SetOnRegisterDirtyBoundary added in v0.1.20

func (c *ContextImpl) SetOnRegisterDirtyBoundary(callback func(key uint64))

SetOnRegisterDirtyBoundary sets the callback for RegisterDirtyBoundary.

The Window wires this during initialization to AddDirtyBoundary, so that upward dirty propagation populates the flat dirty set. This enables O(1) HasDirtyBoundaries checks instead of O(n) tree walks.

func (*ContextImpl) SetOnScheduleAnimation added in v0.1.19

func (c *ContextImpl) SetOnScheduleAnimation(callback func())

SetOnScheduleAnimation sets the callback for ScheduleAnimationFrame.

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 DamageController added in v0.1.19

type DamageController interface {
	SetDamageTracking(enabled bool)
}

DamageController can suppress damage tracking during rendering. Implemented by render.Canvas for retained-mode optimization. RepaintBoundary uses this to suppress damage during cached scene replay.

type DeviceScaler added in v0.1.19

type DeviceScaler interface {
	SetDeviceScale(scale float32)
}

DeviceScaler is an optional interface for canvases that support DPI-aware SVG icon rasterization (ADR-026). When a canvas implements DeviceScaler, the boundary recording infrastructure sets the display scale factor so that SVG icons are rasterized at physical pixel size (ceil(logical * scale)) and drawn with an inverse-scale transform for crisp HiDPI rendering.

Use type assertion to set the scale after creating a recording canvas:

if ds, ok := recorder.(widget.DeviceScaler); ok {
    ds.SetDeviceScale(ctx.Scale())
}

This follows the Qt6/Chromium/IntelliJ pattern where icon assets are rendered at the device's native resolution rather than logical pixel size.

type DirtyBoundaryRegistrar added in v0.1.20

type DirtyBoundaryRegistrar interface {
	RegisterDirtyBoundary(key uint64)
}

DirtyBoundaryRegistrar is an optional interface implemented by Context implementations that support O(1) flat dirty boundary tracking.

During upward dirty propagation, when a RepaintBoundary's onBoundaryDirty callback fires, it type-asserts the Context to DirtyBoundaryRegistrar and registers the boundary in the Window's flat dirty set. This replaces O(n) NeedsRedrawInTreeNonBoundary tree walks with O(1) map lookup.

This is the Flutter _nodesNeedingPaint pattern: a flat list of dirty RenderObjects, populated during markNeedsPaint, consumed during flushPaint.

Example usage in onBoundaryDirty callback:

if reg, ok := ctx.(widget.DirtyBoundaryRegistrar); ok {
    reg.RegisterDirtyBoundary(key)
}

type DirtyTrackerProvider added in v0.1.14

type DirtyTrackerProvider interface {
	DirtyTracker() DirtyTrackerRef
}

DirtyTrackerProvider is an optional interface implemented by Context implementations that provide access to the frame's dirty region tracker.

During a draw pass, widgets like RepaintBoundary type-assert the Context to DirtyTrackerProvider to query spatial dirty regions. This uses the established "interface extension via type assertion" pattern (same as DrawStatsProvider, Focusable) to avoid adding methods to the Context interface (which would be a breaking change).

Example usage in a widget's Draw method:

if provider, ok := ctx.(widget.DirtyTrackerProvider); ok {
    if tracker := provider.DirtyTracker(); tracker != nil {
        if !tracker.Intersects(rb.Bounds()) {
            // Bounds don't overlap any dirty region — guaranteed clean.
            return
        }
    }
}

type DirtyTrackerRef added in v0.1.14

type DirtyTrackerRef interface {
	// Intersects returns true if the given bounds intersect any dirty region.
	// Used by RepaintBoundary for O(regions) early exit: when bounds don't
	// overlap any dirty region, the subtree is guaranteed clean and the
	// expensive O(tree_depth) NeedsRedrawInTree walk can be skipped.
	Intersects(bounds geometry.Rect) bool
}

DirtyTrackerRef is a minimal interface for querying spatial dirty regions during the draw pass. It is defined in the widget package (rather than importing internal/dirty) so that widgets like RepaintBoundary can check whether their bounds intersect any dirty region without depending on internal packages.

The internal dirty.Tracker satisfies this interface through structural typing — no explicit implementation is needed.

type DrawFunc

type DrawFunc func(ctx Context, canvas Canvas)

DrawFunc is a function type for custom drawing logic.

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

func CollectDrawStats(w Widget) DrawStats

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

func DrawTree(w Widget, ctx Context, canvas Canvas) DrawStats

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 DrawStatsProvider added in v0.1.14

type DrawStatsProvider interface {
	DrawStats() *DrawStats
}

DrawStatsProvider is an optional interface implemented by Context implementations that support draw statistics collection.

During a draw pass, widgets like RepaintBoundary type-assert the Context to DrawStatsProvider to record cache hits in the frame's DrawStats. This uses the established "interface extension via type assertion" pattern (same as Focusable, redrawChecker) to avoid adding methods to the Context interface (which would be a breaking change).

Example usage in a widget's Draw method:

if provider, ok := ctx.(widget.DrawStatsProvider); ok {
    if stats := provider.DrawStats(); stats != nil {
        stats.CachedWidgets++
    }
}

type EventFunc

type EventFunc func(ctx Context, e event.Event) bool

EventFunc is a function type for custom event handling.

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 ImageCacheProvider added in v0.1.14

type ImageCacheProvider interface {
	ImageCache() ImageCacheRef
}

ImageCacheProvider is an optional interface implemented by Context implementations that provide a centralized RepaintBoundary pixel cache.

During a draw pass, RepaintBoundary type-asserts the Context to ImageCacheProvider to access the shared LRU cache. This uses the established "interface extension via type assertion" pattern (same as DrawStatsProvider, DirtyTrackerProvider) to avoid adding methods to the Context interface (which would be a breaking change).

When no ImageCacheProvider is available (e.g., in headless tests), RepaintBoundary falls back to its local per-widget cache field.

Example usage in a widget's Draw method:

if provider, ok := ctx.(widget.ImageCacheProvider); ok {
    if cache := provider.ImageCache(); cache != nil {
        img, ok := cache.Get(rb.cacheKey)
        // ...
    }
}

type ImageCacheRef added in v0.1.14

type ImageCacheRef interface {
	// Get retrieves a cached image by key. Returns the image and true if
	// found, nil and false otherwise. On hit, the entry is promoted in LRU.
	Get(key uint64) (*image.RGBA, bool)

	// Put stores an image in the cache with the given key and version.
	// Evicts LRU entries if the memory budget is exceeded.
	Put(key uint64, img *image.RGBA, version uint64)

	// Invalidate removes a specific entry from the cache by key.
	Invalidate(key uint64)
}

ImageCacheRef is a minimal interface for a centralized RepaintBoundary pixel cache with LRU eviction. It is defined in the widget package so that primitives/repaint_boundary.go can use the cache without importing internal/render (which would be an import cycle).

The internal render.ImageCache satisfies this interface through structural typing — no explicit implementation is needed.

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 LineCap added in v0.1.14

type LineCap uint8

LineCap specifies how the endpoints of stroked arcs and lines are drawn.

const (
	LineCapButt   LineCap = iota // flat end, stops exactly at endpoint
	LineCapRound                 // semicircle extending past endpoint
	LineCapSquare                // half-square extending past endpoint
)

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 RepaintBoundaryMarker added in v0.1.14

type RepaintBoundaryMarker interface {
	// MarkBoundaryDirty marks this repaint boundary as needing re-rendering.
	// Called by the upward dirty propagation in [WidgetBase.SetNeedsRedraw].
	MarkBoundaryDirty()
}

RepaintBoundaryMarker is an optional interface implemented by widgets that act as repaint boundaries in the widget tree (ADR-007).

During upward dirty propagation (WidgetBase.SetNeedsRedraw), the parent chain is walked until a widget implementing this interface is found. The boundary is then marked dirty, and propagation stops.

This is the Flutter markNeedsPaint pattern: dirty flags propagate UP to the nearest RepaintBoundary instead of DOWN through the entire tree.

type SVGFiller added in v0.1.4

type SVGFiller interface {
	// FillSVGPath fills an SVG path within the given bounds.
	FillSVGPath(svgData string, viewBox float32, bounds geometry.Rect, color Color)
}

SVGFiller is an optional interface for canvases that support SVG path fill. Use type assertion to check: if f, ok := canvas.(SVGFiller); ok { ... }

type SVGRenderer added in v0.1.4

type SVGRenderer interface {
	// RenderSVG renders full SVG XML within the given bounds with color override.
	RenderSVG(svgXML []byte, bounds geometry.Rect, color Color)
}

SVGRenderer is an optional interface for canvases that support full SVG rendering. Full SVG XML is rasterized to bitmap and drawn at the specified bounds.

type SceneRecorder added in v0.1.19

type SceneRecorder func(s *scene.Scene, width, height int) (Canvas, func())

SceneRecorder creates a recording Canvas that writes draw commands into a scene.Scene. This is the dependency-injection point for ADR-024 Phase 2: the widget package cannot import internal/render (circular dep), so the app layer registers a factory function that creates SceneCanvas instances.

The returned Canvas records all drawing operations into the given scene. After recording, the scene can be replayed via Canvas.ReplayScene.

Parameters:

  • s: the scene.Scene to record into (must not be nil)
  • width, height: dimensions of the recording canvas

Returns a Canvas that records into s, and a cleanup function that must be called after recording is complete (e.g., SceneCanvas.Close).

func GetSceneRecorderFactory added in v0.1.19

func GetSceneRecorderFactory() SceneRecorder

GetSceneRecorderFactory returns the registered SceneRecorder factory. Returns nil if no factory has been registered.

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 StyledTextDrawer added in v0.1.23

type StyledTextDrawer interface {
	// DrawStyledText draws text within the given bounding rectangle using
	// the font specified in the TextStyle. The font is resolved from the
	// global font registry using CSS weight matching.
	DrawStyledText(text string, bounds geometry.Rect, style TextStyle)

	// MeasureStyledText returns the width in pixels of the given text
	// when rendered with the specified TextStyle.
	MeasureStyledText(text string, style TextStyle) float32
}

StyledTextDrawer is an optional interface for canvases that support rendering text with custom fonts from the font registry.

Use type assertion to check availability:

if sd, ok := canvas.(widget.StyledTextDrawer); ok {
    sd.DrawStyledText("Hello", bounds, widget.TextStyle{
        FontFamily: "NotoSansCJK",
        FontSize:   16,
        Color:      widget.ColorBlack,
    })
    width := sd.MeasureStyledText("Hello", widget.TextStyle{
        FontFamily: "NotoSansCJK",
        FontSize:   16,
    })
}

Widgets that need custom font support should check for this interface and fall back to Canvas.DrawText when it is not available. This follows the same optional interface pattern as ArcStroker, SVGFiller, and TextModeController.

type TextAlign

type TextAlign uint8

TextAlign specifies horizontal text alignment within bounds.

const (
	// TextAlignLeft aligns text to the left edge (default).
	TextAlignLeft TextAlign = iota

	// TextAlignCenter centers text horizontally.
	TextAlignCenter

	// TextAlignRight aligns text to the right edge.
	TextAlignRight
)

func (TextAlign) Float64

func (a TextAlign) Float64() float64

Float64 returns the alignment as a float64 value for rendering backends. Left=0, Center=0.5, Right=1.

func (TextAlign) String

func (a TextAlign) String() string

String returns a human-readable name for the text alignment.

type TextMode added in v0.1.19

type TextMode int

TextMode controls text rendering strategy selection.

Maps 1:1 to gg.TextMode. The default TextModeAuto selects the best strategy automatically based on GPU availability, transform, and font size.

const (
	TextModeAuto      TextMode = iota // auto-select based on context
	TextModeMSDF                      // GPU MSDF atlas (games, animations)
	TextModeVector                    // vector outlines (quality-critical)
	TextModeBitmap                    // CPU bitmap (export, static)
	TextModeGlyphMask                 // GPU glyph mask (UI labels, <48px)
)

func (TextMode) String added in v0.1.19

func (m TextMode) String() string

String returns the text mode name.

type TextModeController added in v0.1.19

type TextModeController interface {
	SetTextMode(mode TextMode)
	TextMode() TextMode
}

TextModeController is an optional interface for canvases that support explicit text rendering mode control.

Use type assertion to check availability:

if tc, ok := canvas.(widget.TextModeController); ok {
    tc.SetTextMode(widget.TextModeMSDF)
    defer tc.SetTextMode(widget.TextModeAuto)
}

This is primarily useful during zoom/scale operations where the default auto-selection may cause atlas pressure (issue #94).

On SceneCanvas (RepaintBoundary recording), SetTextMode is a no-op because scene text uses TagText which handles mode selection at replay time.

type TextStyle added in v0.1.23

type TextStyle struct {
	// FontFamily is the font family name (e.g., "Inter", "NotoSansCJK").
	// Empty string falls back to the default embedded font.
	FontFamily string

	// FontSize is the font size in logical pixels.
	FontSize float32

	// Bold selects bold weight (700). When false, regular weight (400) is used.
	Bold bool

	// Italic selects italic style. When false, normal style is used.
	Italic bool

	// Color is the text color.
	Color Color

	// Align controls horizontal text alignment within bounds.
	Align TextAlign
}

TextStyle specifies font properties for styled text rendering.

TextStyle is used with StyledTextDrawer to render text with custom fonts loaded through the plugin system or font registry. When FontFamily is empty, the default embedded Inter font is used.

Example:

style := widget.TextStyle{
    FontFamily: "NotoSansCJK",
    FontSize:   16,
    Bold:       true,
    Color:      widget.ColorBlack,
    Align:      widget.TextAlignLeft,
}

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:

  1. Layout: Calculate size given constraints from parent
  2. Draw: Render the widget to a canvas
  3. 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) BoundaryCacheKey added in v0.1.19

func (w *WidgetBase) BoundaryCacheKey() uint64

BoundaryCacheKey returns the unique monotonic ID for this boundary. Returns 0 if the widget is not a repaint boundary.

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) CachedScene added in v0.1.19

func (w *WidgetBase) CachedScene() *scene.Scene

CachedScene returns the boundary's cached scene, or nil if no cache exists. This is used by DrawTree to replay the scene when the boundary is clean.

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) ClearCompositorClip added in v0.1.19

func (w *WidgetBase) ClearCompositorClip()

ClearCompositorClip removes the compositor clip rect.

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) ClearSceneDirty added in v0.1.19

func (w *WidgetBase) ClearSceneDirty()

ClearSceneDirty resets the sceneDirty flag after the boundary has been re-recorded. Called by the render system after a successful record pass.

func (*WidgetBase) CompositorClip added in v0.1.19

func (w *WidgetBase) CompositorClip() geometry.Rect

CompositorClip returns the screen-space clip rect for this boundary. Used by compositeTextures to skip textures outside the viewport.

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) HasCompositorClip added in v0.1.19

func (w *WidgetBase) HasCompositorClip() bool

HasCompositorClip returns whether a compositor clip rect has been set.

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) InvalidateScene added in v0.1.19

func (w *WidgetBase) InvalidateScene()

InvalidateScene marks this boundary's cached scene as stale, forcing a re-record on the next draw pass. This is called automatically when descendants call SetNeedsRedraw (upward dirty propagation via propagateDirtyUpward).

If this widget is not a repaint boundary, this is a no-op. If the scene is already dirty, this is a no-op (O(1) guard).

Triggers the onBoundaryDirty callback to notify the Window.

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) IsRepaintBoundary added in v0.1.19

func (w *WidgetBase) IsRepaintBoundary() bool

IsRepaintBoundary reports whether this widget is a repaint boundary.

Repaint boundaries own a scene.Scene that caches their subtree rendering. The DrawTree function checks this property and replays the cached scene when the boundary is clean, avoiding re-execution of the child Draw methods.

func (*WidgetBase) IsSceneDirty added in v0.1.19

func (w *WidgetBase) IsSceneDirty() bool

IsSceneDirty reports whether the boundary's cached scene needs re-recording.

func (*WidgetBase) IsScreenOriginValid added in v0.1.19

func (w *WidgetBase) IsScreenOriginValid() bool

IsScreenOriginValid reports whether ScreenOrigin has been set by StampScreenOrigin during a Draw pass. Boundaries with invalid ScreenOrigin (never drawn) should not be composited — their textures would appear at (0,0) instead of the correct position.

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) MarkRedrawLocal added in v0.1.14

func (w *WidgetBase) MarkRedrawLocal()

MarkRedrawLocal sets the needsRedraw flag on this widget only, without propagating upward through the parent chain. Use this for widgets with continuous animations (spinner, progress bar) where only the widget's own bounds should be in the dirty region, not the entire parent subtree.

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) SceneCacheSize added in v0.1.19

func (w *WidgetBase) SceneCacheSize() (int, int)

SceneCacheSize returns the cached scene dimensions (width, height). Returns (0, 0) if no cache exists.

func (*WidgetBase) SceneCacheVersion added in v0.1.19

func (w *WidgetBase) SceneCacheVersion() uint64

SceneCacheVersion returns a monotonic counter that increments each time the boundary's scene is re-recorded. Used by the compositor to detect when content has actually changed between frames.

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) SetCachedScene added in v0.1.19

func (w *WidgetBase) SetCachedScene(s *scene.Scene)

SetCachedScene stores the recorded scene for this boundary. Called by the render system after recording the subtree.

func (*WidgetBase) SetCompositorClip added in v0.1.19

func (w *WidgetBase) SetCompositorClip(clip geometry.Rect)

SetCompositorClip records the screen-space clip rect for this boundary. Called by DrawChild when skipping child boundaries during BoundaryRecording.

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.

When v is true, this also propagates the dirty flag upward through the parent chain to the nearest RepaintBoundary (ADR-007 upward dirty propagation). This is O(depth) rather than the O(n) downward tree walk of NeedsRedrawInTree. If the widget is already marked dirty, the upward propagation is skipped (O(1) guard).

When v is false, only the local flag is cleared (no propagation needed).

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) SetOnBoundaryDirty added in v0.1.19

func (w *WidgetBase) SetOnBoundaryDirty(fn func())

SetOnBoundaryDirty sets the callback invoked when this boundary transitions from clean to dirty via upward propagation. Used by the Window to collect dirty boundaries into its set and request a redraw.

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) SetRepaintBoundary added in v0.1.19

func (w *WidgetBase) SetRepaintBoundary(enabled bool)

SetRepaintBoundary marks this widget as a repaint boundary.

When enabled, the widget owns a scene.Scene display list that caches its subtree rendering. Clean boundaries replay their cached scene instead of re-executing Draw on every descendant.

This is equivalent to Flutter's RenderObject.isRepaintBoundary and Android's View.setLayerType(LAYER_TYPE_HARDWARE).

Calling this with false disables boundary behavior and releases the cached scene.

func (*WidgetBase) SetSceneCacheSize added in v0.1.19

func (w *WidgetBase) SetSceneCacheSize(width, height int)

SetSceneCacheSize records the dimensions of the cached scene. If the widget's bounds change, the caller should invalidate the scene.

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) SetSuppressDirtyCallback added in v0.1.19

func (w *WidgetBase) SetSuppressDirtyCallback(v bool)

SetSuppressDirtyCallback controls whether onBoundaryDirty callback fires during InvalidateScene. Set to true during Draw recording so animated widgets can defer render requests via ScheduleAnimationFrame instead of triggering immediate RequestRedraw.

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.

Jump to

Keyboard shortcuts

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