app

package
v0.1.17 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package app provides the bridge between the UI widget tree and the windowing system.

The app package connects gogpu/ui widgets to a real window via gpucontext interfaces (WindowProvider, PlatformProvider, EventSource). This enables dependency inversion: ui depends on gpucontext interfaces, while gogpu implements those interfaces.

Architecture

App is the main entry point. It holds a Window, theme, and scheduler. Window manages the widget tree and coordinates layout, draw, and event dispatch. EventBridge translates platform-level events from gpucontext into ui/event types that widgets understand.

user code
   |
   v
app.New(opts...)  -->  App  -->  Window  -->  widget tree
                        |            |
                     Theme      EventBridge
                        |            |
                     Scheduler   gpucontext.EventSource

Usage

Create an App with functional options, set a root widget, and let the host application (gogpu.App) drive the render loop:

// In the host application (e.g., gogpu.App's draw callback):
uiApp := app.New(
    app.WithWindowProvider(wp),
    app.WithPlatformProvider(pp),
    app.WithEventSource(es),
)
uiApp.SetRoot(myRootWidget)

// Each frame, the host calls:
uiApp.Frame()

Headless Mode

When no providers are supplied, the App operates in headless mode. This is useful for testing and batch processing:

uiApp := app.New() // headless, no window
uiApp.SetRoot(myWidget)
uiApp.Frame() // performs layout and draw with defaults

Thread Safety

App and Window are NOT safe for concurrent access. All operations must occur on the main/UI thread. This matches the single-threaded event loop model of windowing systems.

Design Principles

  • Dependency inversion: ui imports gpucontext interfaces, not gogpu
  • No goroutines: everything runs on the caller's thread
  • No global state: App is instance-based
  • Testable: mock providers enable isolated testing
  • Composable: functional options for configuration

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App is the main entry point for the UI framework.

App bridges the widget tree with the windowing system through gpucontext interfaces. It manages the Window, theme, and scheduler.

Create an App with New and functional options.

func New

func New(opts ...Option) *App

New creates a new App with the given options.

When no options are provided, the App operates in headless mode with default settings suitable for testing.

func (*App) Frame

func (a *App) Frame()

Frame performs one complete frame: layout, draw, and state management.

This method should be called by the host application's render loop (e.g., gogpu.App's draw callback).

func (*App) HandleEvent

func (a *App) HandleEvent(e event.Event)

HandleEvent dispatches a single event to the widget tree.

This method is an alternative to using an EventSource. It allows the host application to push events manually.

func (*App) Scheduler

func (a *App) Scheduler() *state.Scheduler

Scheduler returns the App's state scheduler.

func (*App) SetFrameCallback

func (a *App) SetFrameCallback(cb FrameCallback)

SetFrameCallback sets a callback that is invoked after each Frame call with timing statistics.

Set to nil to disable statistics tracking. The callback is called synchronously on the same goroutine as Frame().

func (*App) SetRoot

func (a *App) SetRoot(root widget.Widget)

SetRoot sets the root widget for the App's window.

The root widget forms the top of the widget tree. It receives layout constraints matching the window size and is drawn to fill the window.

func (*App) SetTheme

func (a *App) SetTheme(t *theme.Theme)

SetTheme changes the App's theme.

This updates both the App and Window theme and triggers a full redraw.

func (*App) Theme

func (a *App) Theme() *theme.Theme

Theme returns the App's current theme.

func (*App) Window

func (a *App) Window() *Window

Window returns the App's Window.

type FrameCallback

type FrameCallback func(stats FrameStats)

FrameCallback is called after each frame completes with timing statistics.

This is useful for frame time monitoring and adaptive rendering. The callback is invoked synchronously at the end of each Frame() call.

type FrameStats

type FrameStats struct {
	// FrameStart is the time when Frame() was called.
	FrameStart time.Time

	// LayoutDuration is how long the layout pass took.
	// Zero if layout was not performed this frame.
	LayoutDuration time.Duration

	// DrawDuration is how long the draw pass took.
	DrawDuration time.Duration

	// TotalDuration is the total time spent in Frame().
	TotalDuration time.Duration

	// LayoutPerformed indicates whether layout was recalculated this frame.
	LayoutPerformed bool

	// DrawSkipped indicates that the draw pass was skipped because no
	// widgets in the tree needed re-rendering. When true, the host
	// application can reuse the previous frame's GPU framebuffer output.
	//
	// This is the primary retained-mode optimization: idle UIs skip
	// rendering entirely, consuming zero CPU for the draw phase.
	DrawSkipped bool

	// DrawStats provides per-widget statistics from the draw traversal.
	//
	// When DrawSkipped is true, DrawStats is zero-valued (no traversal
	// was performed). When DrawSkipped is false, DrawStats shows how many
	// widgets were drawn, how many were dirty vs clean, and how many were
	// skipped due to invisibility.
	//
	// This is primarily useful for performance monitoring and for
	// validating that the retained-mode dirty-tracking system is
	// working correctly.
	DrawStats widget.DrawStats
}

FrameStats holds timing information for a single frame.

This is useful for performance monitoring and debugging. FrameStats is passed to the FrameCallback after each call to App.Frame or Window.Frame.

type Option

type Option func(*appConfig)

Option configures an App during creation.

func WithEventSource

func WithEventSource(es gpucontext.EventSource) Option

WithEventSource sets the event source for the App.

The EventSource delivers input events (keyboard, mouse, scroll, resize, focus) from the host application. When nil, no events are delivered.

func WithPlatformProvider

func WithPlatformProvider(pp gpucontext.PlatformProvider) Option

WithPlatformProvider sets the platform provider for the App.

The PlatformProvider provides OS integration features such as clipboard, cursor management, and accessibility preferences. When nil, these features are unavailable.

func WithRenderMode added in v0.1.14

func WithRenderMode(mode RenderMode) Option

WithRenderMode sets the rendering mode for the App's window.

RenderModeHostManaged (default): the host application draws the background before calling DrawTo. DrawTo does not call canvas.Clear and always draws the full widget tree.

RenderModeFrameworkManaged: the framework owns the pixmap and draws the theme background. Enables frame skip (DrawTo returns false when idle) and incremental dirty-region rendering.

See RenderMode for detailed documentation of each mode.

func WithTheme

func WithTheme(t *theme.Theme) Option

WithTheme sets the theme for the App.

When nil, the default light theme is used.

func WithWindowProvider

func WithWindowProvider(wp gpucontext.WindowProvider) Option

WithWindowProvider sets the window provider for the App.

The WindowProvider provides window geometry (Size, ScaleFactor) and the ability to request redraws. When nil, the App operates in headless mode with a default 800x600 window at 1x scale.

type RenderMode added in v0.1.14

type RenderMode int

RenderMode controls who owns the window background and pixmap lifecycle.

This is the primary configuration point for choosing between host-managed and framework-managed rendering. The mode affects whether DrawTo clears the canvas, whether frame skip is possible, and whether incremental dirty-region rendering is active.

Enterprise GUI frameworks (Qt QBackingStore, Flutter, Win32) formalize background ownership through explicit API. RenderMode provides the same clarity for gogpu/ui.

const (
	// RenderModeHostManaged is the default rendering mode where the host
	// application owns the window background and pixmap lifecycle.
	//
	// In this mode:
	//   - The host draws its own background before calling DrawTo.
	//   - DrawTo does NOT call canvas.Clear (the host already painted the background).
	//   - DrawTo always performs a full widget tree draw via DrawTree.
	//   - DrawTo always returns true (a valid frame is always produced).
	//   - Frame skip is NOT possible (host may have cleared the pixmap).
	//   - dirty.Tracker is still populated (for RepaintBoundary Intersects fast path).
	//
	// This is backward compatible with existing host applications that
	// clear the canvas themselves before calling DrawTo.
	RenderModeHostManaged RenderMode = iota

	// RenderModeFrameworkManaged is the rendering mode where the framework
	// owns the pixmap lifecycle and draws the theme background.
	//
	// In this mode:
	//   - The host does NOT draw background before calling DrawTo.
	//   - DrawTo calls canvas.Clear(themeBackground) on full repaint.
	//   - Frame skip: DrawTo returns false when nothing changed (Level 1, ADR-004).
	//   - Incremental rendering: only dirty regions are redrawn (Level 2, ADR-004).
	//   - Full repaint on: first frame, resize, theme change, SetRoot.
	//
	// This mode enables the full three-level incremental rendering pipeline
	// described in ADR-004, achieving near-zero CPU for idle UIs.
	RenderModeFrameworkManaged
)

func (RenderMode) String added in v0.1.14

func (m RenderMode) String() string

String returns a human-readable name for the render mode.

type Window

type Window struct {
	// contains filtered or unexported fields
}

func (*Window) AddDirtyBoundary added in v0.1.14

func (w *Window) AddDirtyBoundary(key uint64, boundary widget.RepaintBoundaryMarker)

AddDirtyBoundary registers a RepaintBoundary as dirty. Called by the onBoundaryDirty callback during upward dirty propagation.

The key parameter is the boundary's unique cache key for deduplication. If the boundary is already in the set, this is a no-op.

func (*Window) BoundaryDamageRegion added in v0.1.14

func (w *Window) BoundaryDamageRegion() geometry.Rect

BoundaryDamageRegion computes the union of screen bounds of all dirty RepaintBoundary instances. This provides a tighter damage region for the compositor when only specific boundaries changed (ADR-007 Phase 3, Task 3d).

Returns a zero Rect when no boundaries are dirty.

The compositor can use min(BoundaryDamageRegion, LastDirtyUnion) to get the tightest possible damage region for scissored GPU present.

func (*Window) ClearDirtyBoundaries added in v0.1.14

func (w *Window) ClearDirtyBoundaries()

ClearDirtyBoundaries resets the dirty boundary set after painting. Each boundary's ClearBoundaryDirty is NOT called here — that is the responsibility of the PaintDirtyBoundaries method.

func (*Window) Context

func (w *Window) Context() *widget.ContextImpl

Context returns the widget context used for layout, draw, and events.

func (*Window) DirtyBoundaryCount added in v0.1.14

func (w *Window) DirtyBoundaryCount() int

DirtyBoundaryCount returns the number of dirty RepaintBoundary instances.

func (*Window) DirtyRegionCount added in v0.1.14

func (w *Window) DirtyRegionCount() int

DirtyRegionCount returns the number of dirty regions from the most recent DrawTo call. This reflects the state after Collector.Collect + Tracker.Optimize in the last frame.

Returns 0 when the last frame was a full repaint (no incremental regions) or when the frame was skipped (nothing dirty).

This is an observability hook for monitoring incremental rendering efficiency.

func (*Window) DrawTo

func (w *Window) DrawTo(canvas widget.Canvas) bool

DrawTo performs the draw pass using the provided canvas.

Behavior depends on the RenderMode:

In RenderModeHostManaged (default):

  • The host draws background before calling DrawTo.
  • DrawTo does NOT call canvas.Clear (host already painted background).
  • DrawTo always draws the full widget tree and returns true.
  • dirty.Tracker is populated for RepaintBoundary Intersects fast path.

In RenderModeFrameworkManaged:

  • Level 1 (Frame Skip): If nothing changed since the last draw, returns false immediately — zero CPU, zero GPU upload.
  • Level 2 (Dirty Region Redraw): Only dirty regions are redrawn on the persistent pixmap. Clean regions retain previous pixels (Qt QBackingStore pattern).
  • Full Repaint: On first frame, resize, theme change, or SetRoot, the entire pixmap is cleared and redrawn.

Returns true if rendering was performed, false if the draw was skipped.

func (*Window) FocusManager

func (w *Window) FocusManager() *ifocus.Manager

FocusManager returns the window's focus manager.

The focus manager handles Tab/Shift+Tab navigation between focusable widgets and supports registering global keyboard shortcuts.

func (*Window) Frame

func (w *Window) Frame()

Frame performs one complete frame cycle.

The frame cycle consists of:

  1. Update time tracking
  2. Flush pending scheduler updates
  3. Perform layout if needed
  4. Draw the widget tree
  5. Sync cursor state to platform
  6. Clear invalidation flags

Frame is a no-op if there is no root widget.

func (*Window) HandleEvent

func (w *Window) HandleEvent(e event.Event)

HandleEvent dispatches a single event to the widget tree.

Events are first offered to the overlay stack (top overlay has priority). If no overlay consumes the event (and no modal overlay blocks it), key events are offered to the focus manager for Tab/Shift+Tab navigation and registered shortcuts. Finally, unconsumed events are propagated to the root widget.

func (*Window) HandleFocusChange

func (w *Window) HandleFocusChange(focused bool)

HandleFocusChange processes a window focus change.

func (*Window) HandleResize

func (w *Window) HandleResize(width, height int)

HandleResize processes a window resize.

This updates the window size and marks layout as needing recalculation.

func (*Window) HasDirtyBoundaries added in v0.1.14

func (w *Window) HasDirtyBoundaries() bool

HasDirtyBoundaries reports whether any RepaintBoundary has been marked dirty since the last paint pass.

func (*Window) HasDirtyBoundariesOrNeedsRedraw added in v0.1.14

func (w *Window) HasDirtyBoundariesOrNeedsRedraw() bool

HasDirtyBoundariesOrNeedsRedraw reports whether any rendering work is needed: either dirty boundaries from upward propagation or full-frame redraw flags (needsRedraw, needsFullRepaint).

func (*Window) HoveredWidget

func (w *Window) HoveredWidget() widget.Widget

HoveredWidget returns the widget currently under the mouse pointer, or nil if no widget is hovered.

func (*Window) LastDirtyUnion added in v0.1.14

func (w *Window) LastDirtyUnion() geometry.Rect

LastDirtyUnion returns the union of all dirty regions from the most recent dirty-region-only repaint. Returns a zero Rect when the last frame was a full repaint or a frame skip.

Used by desktop.Run to pass the dirty region to ggcanvas for partial texture upload — only the modified region is uploaded to the GPU instead of the entire pixmap.

func (*Window) LastDrawStats

func (w *Window) LastDrawStats() widget.DrawStats

LastDrawStats returns the per-widget statistics from the most recent draw traversal (either via Window.DrawTo or the headless draw path inside Window.Frame).

When the last frame was skipped (no dirty widgets), the returned stats are zero-valued.

func (*Window) NeedsLayout

func (w *Window) NeedsLayout() bool

NeedsLayout returns true if layout needs recalculation.

func (*Window) NeedsRedraw

func (w *Window) NeedsRedraw() bool

NeedsRedraw reports whether any widget in the tree needs re-rendering.

When this returns false, the host application can skip calling Window.DrawTo and reuse the previous frame's output from the GPU framebuffer. This is the primary optimization of retained-mode rendering: idle UIs consume zero CPU for rendering.

func (*Window) Overlays

func (w *Window) Overlays() *overlay.Stack

Overlays returns the window's overlay stack.

func (*Window) PaintDirtyBoundaries added in v0.1.14

func (w *Window) PaintDirtyBoundaries()

PaintDirtyBoundaries clears the dirty boundary set after a frame.

RepaintBoundary.Draw() handles cache invalidation internally: when boundaryDirty is true, it re-records child.Draw() into its scene.Scene. The full DrawTree pass triggers re-recording automatically — dirty boundaries re-record, clean ones replay cached scenes via ReplayScene.

This is the Flutter flushPaint pattern: only dirty RepaintBoundary nodes re-record, clean ones replay cached scenes.

func (*Window) RenderMode added in v0.1.14

func (w *Window) RenderMode() RenderMode

RenderMode returns the window's current rendering mode.

func (*Window) Root

func (w *Window) Root() widget.Widget

Root returns the current root widget, or nil if none is set.

func (*Window) SetRenderMode added in v0.1.14

func (w *Window) SetRenderMode(mode RenderMode)

SetRenderMode changes the window's rendering mode at runtime.

Switching to RenderModeFrameworkManaged enables frame skip and dirty-region rendering. Switching to RenderModeHostManaged restores full-tree draw every frame.

A full repaint is forced after the switch to ensure the persistent pixmap is populated correctly in the new mode.

func (*Window) SetRoot

func (w *Window) SetRoot(root widget.Widget)

SetRoot sets the root widget for this window.

Setting a new root triggers a full layout on the next Frame call. The old root tree is unmounted and the new root tree is mounted, which triggers signal binding setup/teardown via widget.Lifecycle.

func (*Window) Theme

func (w *Window) Theme() *theme.Theme

Theme returns the window's current theme.

func (*Window) ThemeBackground added in v0.1.14

func (w *Window) ThemeBackground() widget.Color

ThemeBackground returns the window background color from the current theme. Falls back to white if no theme is configured.

func (*Window) WasFullRepaint added in v0.1.14

func (w *Window) WasFullRepaint() bool

WasFullRepaint returns true if the most recent DrawTo performed a full repaint (first frame, resize, theme change, SetRoot). When true, the entire pixmap was modified and needs full GPU upload.

func (*Window) WindowSize

func (w *Window) WindowSize() geometry.Size

WindowSize returns the current window size in logical pixels.

Jump to

Keyboard shortcuts

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