VirtualList
ui.widgets.VirtualList大量の等高データのうち、表示範囲だけをマウントします。
BEHAVIOR CONTRACT
item_count、item_height、overscan、再利用、scroll offset によって O(visible) の安定したコストになります。
RECORDED INTERACTION
harness がリストをスクロールし、ノードは表示範囲だけをカバーします。
pub fn buildVirtualList(scope: *ui.Scope, cx: *ui.Cx) anyerror!*ui.Node {
const vl = try W.VirtualList(.{ .item_count = 1000, .item_height = 28, .width = 320, .height = 360 }).mount(scope, cx, vlRender);
return vl.container;
}Storybook にあるこの story のコードそのままです(zenit 5f9add5+wip 2026-09-30)。W は ui.widgets のことで、col / row / label は Storybook 内の小さなレイアウトヘルパーです。
VirtualListProps
ui.widgets.VirtualListfn VirtualList(props: VirtualListProps) VirtualListBuilderフィールド
型
デフォルト値
説明
item_countusize0总 item 数量
item_heightf3232每个 item 的固定高度(px)
width?f32null容器宽度
height?f32null容器高度
overscanusize5视口外额外渲染的行数(上下各 overscan 行)
paddingPaddingPadding.ZERO内边距
backgroundColorColor.TRANSPARENT容器背景色
scroll_speedf321滚动速度乘数
scroll_directionScrollDirection.vertical.horizontal.both.vertical滚动方向(默认纵向)
content_width?f32null内容宽度(用于横向滚动,null=由布局自动决定)
item_height_fn?*const fn (index: usize, user_context: ?*anyopaque) f32null可选:按 item 返回高度(px)。给了它就进入**不等高模式**。 null(默认)→ 所有 item 用 `item_height`,与以前完全一致。 非 null → 总高、可见范围、spacer、ensureVisible 全部改走前缀和。 回调必须是纯函数且对同一 index 稳定:它每帧会被调用若干次,返回值 若在同一帧内变化,滚动几何会自相矛盾。
item_height_context?*anyopaquenull传给 `item_height_fn` 的上下文(通常与 render 的 user_context 同一个)。
measure_itemsboolfalse**动态测量模式**:行高由内容自己决定,组件量出来再记账。 三种高度模式的取舍: - 默认(都不设)→ 等高,`item_height`,O(1) 定位,最快。 - `item_height_fn` → 不等高但**高度可预先算出**(如 diff 行:代码行 20、 hunk 头 28)。不需要测量,几何一次到位。 - `measure_items = true` → 不等高且**高度事先不知道**(如自动换行的 评论、聊天气泡)。行先按 `estimate_item_height` 占位,布局后读回真实 高度并回填,同时补偿滚动位置。 同时设了 `item_height_fn` 时以 `item_height_fn` 为准(它更便宜且精确)。
estimate_item_heightf3232未测量行的占位高度。估得越准,滚动条抖动越小。
item_key_fn?*const fn (index: usize, user_context: ?*anyopaque) measurements.ItemKeynull可选:index → 稳定 key。给了它,实测高度就跟着**数据**走而不是跟着 位置走 —— 在头部插入一条时,后面所有行的实测值依然有效。 null = 用 index 本身(头部增删会让后续行重新测量)。
item_key_context?*anyopaquenull传给 `item_key_fn` 的上下文(null 时复用 render 的 user_context)。
MountResult
ui.widgets.VirtualListmount は MountResult を直接返します。親ノードに append してください。