Skip to content

feat(virtual): live-data list & grid virtualization (VirtualList/VirtualGrid) - #166

Merged
tannevaled merged 1 commit into
mainfrom
feat/virtual-widgets
Aug 7, 2026
Merged

feat(virtual): live-data list & grid virtualization (VirtualList/VirtualGrid)#166
tannevaled merged 1 commit into
mainfrom
feat/virtual-widgets

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What

New github.com/go-widgets/toolkit/virtual sub-package implementing P2 list-virtualization enrichments on top of the existing toolkit windowing: live-model binding, a 2-D recycled grid (gengrid), and variable row heights.

API

type VirtualList[T any] struct {
    Model     *mvvm.ObservableList[T]
    RowHeight func(i int) int   // constant fn = uniform O(1); varying = Fenwick O(log n)
    Render    func(p painter.Painter, th *toolkit.Theme, r toolkit.Rect, i int, item T)
}
type VirtualGrid[T any] struct {
    Model    *mvvm.ObservableList[T]
    CellSize toolkit.Size
    Render   func(p painter.Painter, th *toolkit.Theme, r toolkit.Rect, i int, item T)
}

Both implement toolkit.Widget (Draw/OnEvent/Bounds/SetBounds via toolkit.Base), subscribe to the model's ListEvent stream, keep the scroll anchor stable on inserts/removes above the viewport, and reuse painter.Clipper for the partially-visible trailing item.

How

  • Offset -> first-visible-row is O(1) for uniform heights and O(log n) via a Fenwick / binary-indexed prefix-sum tree for variable heights, with a parallel heights slice giving O(1) per-row height in Draw.
  • VirtualList anchoring is pixel-precise (via the prefix sums); VirtualGrid tracks a persistent top-left anchor item so a run of small inserts above the viewport accumulates exactly instead of quantising to row boundaries.

Tests (100% statement coverage incl. error branches)

  • 1,000,000-item model -> Render invoked exactly visibleCount (50 at H=1000, uniform 20px), verified at top and deep-scrolled.
  • Variable-height first-visible-row checked against a brute-force prefix scan at every pixel offset.
  • Fenwick scroll tick within ~2x uniform on a bench (measured ~2.5x, full frame incl. VisibleRange + per-row height work).
  • 0 allocations per scroll tick (testing.AllocsPerRun).
  • Incremental Insert/Remove/Move/Replace/Reset anchor behaviour (region unchanged on insert-above).
  • gengrid reflow: exact visible-cell count + exact cell rects before and after scroll.

Gates (all green locally)

go vet clean, gofmt clean, 100% coverage across the whole module, -race (CGO=1) green, cross-compiles for all 6 64-bit arches (amd64/arm64/riscv64/loong64/ppc64le/s390x) + js/wasm + darwin/arm64 + windows/amd64.

Adds a direct dependency on github.com/go-widgets/mvvm v0.5.0 (already a transitive requirement; the root mvvm package imports nothing from toolkit, so there is no package import cycle).

🤖 Generated with Claude Code

…ualGrid)

Add a `virtual` sub-package that binds scrollable views to a live
mvvm.ObservableList and realises ONLY the items intersecting the viewport,
so a million-item model costs the same per frame as a handful.

- VirtualList[T]: per-row draw callback + optional VARIABLE row heights.
  Scroll offset -> first-visible-row is O(1) for uniform heights and
  O(log n) via a Fenwick (binary-indexed) prefix-sum tree otherwise, with a
  parallel heights slice for O(1) per-row height in Draw.
- VirtualGrid[T]: gengrid analogue -- reflows N uniform cells into as many
  columns as the width allows and realises only the visible cells.

Both implement toolkit.Widget (embedding toolkit.Base), subscribe to the
model's ListEvent stream and keep the scroll anchor stable across
inserts/removes above the viewport (VirtualList pixel-precise via the prefix
sums; VirtualGrid via a persistent top-left anchor item), reusing
painter.Clipper to trim the partially-visible edge items.

Tests: 100% statement coverage incl. error branches; 1,000,000-item model
renders exactly visibleCount rows; variable-height first-visible-row checked
against a brute-force prefix scan at every offset; Fenwick scroll tick within
~2x uniform on a bench; 0 allocations per scroll tick; incremental
Insert/Remove/Move/Replace/Reset anchor behaviour; gengrid reflow exact
visible-cell count + exact cell rects.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit a96c315 into main Aug 7, 2026
1 check passed
@tannevaled
tannevaled deleted the feat/virtual-widgets branch August 7, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant