This document captures design and implementation notes for SelectionList<T>.
For end-user usage and examples, see SelectionList.
SelectedIndex controls the focused row (navigation)Checked is a parallel list of booleans (multi-select state)IScrollable with an internal ScrollModel.DataTemplate<T> to render item content, with recycling via TryUpdate / Release.SelectionList<T> : Visual, IScrollable (sealed)Items : BindableList<T>Checked : BindableList<bool>
Items count (missing entries default to false)SelectedIndex : int
-1 when empty, otherwise [0..Items.Count-1]ItemTemplate : DataTemplate<T>AddItem(T item, bool isChecked = false) appends to both Items and Checked.Scroll : ScrollModelThe control maintains the invariant:
Checked.Count == Items.CountThis is enforced via EnsureCheckedCount() and is called from:
Measure, Render)When items are removed, trailing entries in Checked are removed accordingly.
PrepareChildren snapshots scroll state:
ScrollVersion = _scroll.VersionThis is the standard pattern used to avoid “read then write the same bindable” violations when Arrange updates the scroll model.
Measure computes a prefix area and then measures the item visuals:
runeWidth(SelectionListStyle.FocusMarkerGlyph)max(runeWidth(CheckedGlyph), runeWidth(UncheckedGlyph))SelectionListStyle.SpaceBetweenGlyphAndTextMaxWidth = Infinite, MaxHeight = 1 and takes the max widthMeasuredContentWidth = prefixWidth + maxItemWidthmax(1, Items.Count)When height is bounded and vertical overflow is expected, it reserves extra width for a vertical scrollbar using ScrollViewerStyle.ScrollBarThickness to avoid horizontal overflow feedback loops.
_ = ScrollVersion and ensures item visuals exist.(innerWidth, innerHeight)(max(innerWidth, MeasuredContentWidth), itemCount)x = innerLeft + prefixWidth - OffsetXy = innerTop + (itemIndex - OffsetY)width = extentWidth - prefixWidthheight = 1The list paints its own chrome, then items render themselves:
SelectionListStyle.ResolveItemStyle(theme, enabled, selectedRow, focused)FocusMarkerGlyph)Checked[itemIndex]Up/Down/Home/End/PageUp/PageDown updates SelectedIndexSpace / Enter flips Checked[SelectedIndex]Ctrl+A sets all Checked[i] = trueCtrl+I toggles all Checked[i]SelectedIndex by one rowKey knobs:
FocusMarkerGlyph (default →)CheckedGlyph / UncheckedGlyph (defaults ☑ / ☐)SpaceBetweenGlyphAndTextItem, SelectedFocused, SelectedUnfocused, Disabledsrc/XenoAtom.Terminal.UI.Tests/SelectionListTests.cssrc/XenoAtom.Terminal.UI.Tests/ListControlHorizontalScrollViewerTests.csChecked list for ergonomics.