Skip to content

VirtualList

virtualList · Containers · interactive

A list that builds only what is on screen.

Import

cpp
#include "gbui/widgets/virtualList.hpp"

Example

Nothing is downloaded until you press it.

cpp
VirtualSlice virtualList(Ui& ui, const Interaction& input, std::string_view id, ScrollState& state, const VirtualListOptions& options, const std::function<void(Ui&, std::size_t)>& row);

A list that builds only what is on screen. The trick is not the clipping — the scroll view already clips. It is that the rows that are not visible are replaced by two spacers, one standing in for everything above the slice and one for everything below. The content is therefore the full height it would have been, so the scrollbar, maxOffset and Page Down all keep working on the real list, while the arena holds the forty rows a person can actually see. VirtualSlice shown = virtualList(ui, input, "history", state, {.count = commits.size(), .rowHeight = 28.0f}, [&](Ui& ui, std::size_t index) { listRow(ui, input, rowId(index), …); }); row is called once per visible index, in order, inside a box of exactly rowHeight. It must not open a scope it leaves open. The viewport is last frame's, like everything else that needs geometry before layout has run, so the first frame after a resize builds the previous size's slice — and overscan is what keeps that from showing.

API

VirtualListOptions

OptionTypeDefaultWhat it does
countstd::size_t0How many rows exist. Only the visible ones are ever built.
rowHeightfloat28.0fEvery row is exactly this tall. The list enforces it rather than trusting the caller: a row that laid itself out taller would slide the ones after it out of step with the scrollbar.
gapfloat0.0f
paddingEdges
overscanstd::size_t2Rows built above and below the viewport. Two is enough to keep a fast drag from showing an edge, and cheap enough not to think about.
stepfloat48.0f
scrollbarbooltrueWhether the container underneath draws one.
scrollbarWidthfloat10.0f
autoHideScrollbarbooltrueIt fades out when nothing is pointing at it, the way an overlay bar does.
focusablebooltrue
growfloat1.0f
widthfloatkAuto
heightfloatkAuto
namestd::string_viewWhat the list is of — "Commits". Every row reports its position out of count, so a reader hears "3 of 50 000" and not "3 of the fourteen that happen to be built".
itemRoleRoleRole::ListItemWhat one slot is, to a reader. ListItem and its "n of count" is right for a list. None hands the whole question to the row callback, which is what a hierarchy needs: a tree's rows are TreeItems counted among their siblings, not among the fifty thousand the list happens to hold, and a slot that announced both would be a row inside a row saying two different numbers.

Released under the LGPL-3.0-or-later licence.