README.md
llimphi-widget-lazy-list
A turnkey lazy list.
It attacks the "scroll without laziness" gap: list/grid/table build every
row handed to them, and scroll_y receives content already built — so
mounting 1M rows measured ~4 s/frame, because the whole tree is rebuilt each
frame.
This widget inverts the contract: instead of a ready-made content: View, it
takes a builder item: Fn(usize) -> View<Msg> plus total, and only calls
item(i) for the visible window + overscan. Off-screen rows do not exist: not
built, not mounted, not laid out. The per-frame cost becomes ∝ what is visible,
not ∝ the total.
True to the Elm loop it is stateless: the offset (px) lives in the caller's
Model, just as in llimphi_widget_scroll, whose scrollbar and physics (thumb,
page jump, fling, rubber band) are reused as-is — this crate only adds the real
virtualization that was missing.
Two extent models:
lazy_list_fixed— rows of uniform height (item_h). O(1) window. Covers 90% of cases (logs, table rows, file lists).lazy_list_varied— per-row height, through an array of accumulated offsets precomputed by the caller (offsets[i]= top of rowiin content coordinates;offsets[total]= total height). O(log n) window by binary search; the caller rebuildsoffsetswhen the data or the heights change, not every frame.
// view (fixed item_h):
lazy_list_fixed(
model.rows.len(), // total
28.0, // item_h
model.offset, // offset px (in the Model)
panel_h, // viewport_h
DEFAULT_OVERSCAN,
|i| row_view(&model.rows[i]), // only called for what is visible
Msg::ScrollBy, // Fn(f32)->Msg, delta px
&ScrollPalette::default(),
)
// update:
Msg::ScrollBy(d) => {
m.offset = clamp_offset(m.offset + d, m.rows.len() as f32 * 28.0, panel_h);
}
Part of llimphi — see llimphi.