widget/draw.go

Functions Structs Interfaces

Functions

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 CollectDrawStats(w Widget) DrawStats {
	var stats DrawStats
	collectStatsRecursive(w, &stats)
	return stats
}

func DrawChild

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 DrawChild(child Widget, ctx Context, canvas Canvas) {
	if child == nil {
		return
	}

	type boundaryChecker interface {
		IsRepaintBoundary() bool
	}
	if bc, ok := child.(boundaryChecker); ok && bc.IsRepaintBoundary() {
		// Flutter paintChild: skip child boundaries during parent recording.
		// Each child boundary has own scene + offscreen texture (depth > 1).
		// Compositor blits all textures. StampScreenOrigin already called
		// by container before DrawChild for hitTest correctness.
		if br, ok2 := canvas.(BoundaryRecorder); ok2 && br.IsBoundaryRecording() {
			StampScreenOrigin(child, canvas)
			stampCompositorClip(child, canvas)
			return
		}
		drawBoundaryWidget(child, ctx, canvas, nil)
		return
	}

	child.Draw(ctx, canvas)
}

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.

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

	// Make stats accessible to widgets (e.g., RepaintBoundary) via type
	// assertion on the context during the draw pass.
	type drawStatsSetter interface {
		SetDrawStats(*DrawStats)
	}
	if setter, ok := ctx.(drawStatsSetter); ok {
		setter.SetDrawStats(&stats)
		defer setter.SetDrawStats(nil)
	}

	drawTreeRecursive(w, ctx, canvas, &stats)
	return stats
}

Structs

type DrawStats struct

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.

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
}

Interfaces

type DrawStatsProvider interface

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 DrawStatsProvider interface {
	DrawStats() *DrawStats
}