Skip to main content

Module repeater

Module repeater 

Source
Expand description

Repeater — non-virtualized dynamic widget list driven by a ListModel<T>.

Repeater creates one child widget per item in a ListModel<T> using a caller-supplied factory closure, arranging them along one axis (RepeaterLayout::Vertical by default) or as a wrapping flow (RepeaterLayout::Wrap). It is not virtualized: every item has a live widget at all times. That is a deliberate trade — it is what lets the children keep real, stateful widgets (text editors, forms) mounted, which a virtualizing ListView cannot do because it recycles off-screen rows.

§Repeater::new — reconciling (the default)

The factory takes &item and each child widget is reused across model changes. When the model mutates, Repeater reads the DataChange it emits and applies the minimal edit to its child set: an insert builds one new widget, a remove reaps one, a move reorders, an in-place update rebuilds only that item — every other child keeps its existing widget, and with it its focus, selection, caret, scroll offset, in-flight text edit, and undo history.

This makes Repeater a fit for a stack of editors — e.g. a document rendered as a column of RichTextEditors, one per scene/block:

Repeater::new(scenes, |scene| {
    Box::new(RichTextEditor::editor(scene.document()))
})

Inserting, deleting, or reordering a scene costs one widget’s worth of work instead of reshaping every editor in the document, and the editor the user is typing in keeps its caret. Because the factory has no index, position shifts are safe by construction: reuse can never leave a widget showing content derived from a stale position. The one requirement is that an item’s content only changes through the model (via set/replace_all), which is always true for a ListModel.

let model: ListModel<u32> = ListModel::from_vec(vec![1, 2, 3]);
let _w = Repeater::new(model, |item| {
    Box::new(TextWidget::new(lit!(format!("item {item}"))))
})
.spacing(4.0);

§Repeater::indexed — full rebuild (position-in-content)

When the content genuinely depends on position — a numbered list, “N of M”, a ranking that must renumber on reorder — use indexed. Its factory takes (index, &item), and on any model change the whole child subtree is torn down and rebuilt, so the index every widget shows is always current. This is the right pick for cheap, stateless, position-derived rows; it does not preserve per-child state across changes (that is the reason to prefer new whenever the index isn’t content).

§Accessibility

Repeater imposes no accessibility semantics of its own — it is a transparent layout wrapper, so its children surface directly into the surrounding AT subtree and their own roles decide how they read. When the children genuinely form a named list, menu, or toolbar, opt in with the standard builder overrides that every widget supports — these stay locale-reactive:

use teksilo_core::accesskit::Role;
Repeater::new(tags, factory)
    .access_role(Role::List)
    .access_label(tr!(tags()))

Structs§

Repeater
A non-virtualized dynamic collection that creates one child widget per item in a ListModel<T>.

Enums§

RepeaterLayout
How a Repeater arranges its item widgets.