This document captures design and implementation notes for RadioButtonList<T>.
End-user documentation is currently covered under RadioButton (list variants are demonstrated in the ControlsDemo).
IScrollable with an internal ScrollModel.DataTemplate<T> and a simple recycle pool to reduce allocations when the item list changes.RadioButtonList<T> : Visual, IScrollable (sealed)Items : BindableList<T>SelectedIndex : int
-1 when empty, otherwise [0..Items.Count-1]ItemTemplate : DataTemplate<T>
DataTemplates for role DisplayScroll : ScrollModelThe list maintains:
_itemVisuals : BindableList<Visual> (children)_recyclePool : List<Visual> (detached visuals eligible for reuse)_lastItemsVersion + _lastResolvedTemplate (to avoid rebuilds when nothing changed)Build rules per item:
T is already a Visual, it is used directlyTextBlock(() => ToStringObject(value))DataTemplateValue<T>(value)DataTemplate.TryUpdateDataTemplate.DisplayPrepareChildren snapshots the scroll model version into a bindable:
ScrollVersion = _scroll.VersionArrange and Render then read ScrollVersion as a dependency while still being able to update the underlying ScrollModel safely.
checkWidth = max(runeWidth(CheckedGlyph), runeWidth(UncheckedGlyph))prefixWidth = checkWidth + SpaceBetweenGlyphAndTextMaxWidth = Infinite, MaxHeight = 1 and takes the max width.MeasuredContentWidth = prefixWidth + maxItemWidthmax(1, itemCount)When height is bounded and vertical overflow is expected, it reserves extra width for a vertical scrollbar using ScrollViewerStyle.ScrollBarThickness to avoid “vertical bar reduces viewport ⇒ horizontal bar appears” 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 renders its chrome; items render themselves:
Style.None then per-row style.RadioButtonListStyle.ResolveItemStyle(...)CheckedGlyph or UncheckedGlyph at the glyph columnUp/Down/Home/End/PageUp/PageDown updates SelectedIndex.Selection changes set a flag so Arrange scrolls the selection into view.
Key knobs:
CheckedGlyph / UncheckedGlyph (defaults match RadioButtonStyle)SpaceBetweenGlyphAndTextItem, SelectedFocused, SelectedUnfocused, Disabledsrc/XenoAtom.Terminal.UI.Tests/RadioButtonListTests.cssrc/XenoAtom.Terminal.UI.Tests/ListControlHorizontalScrollViewerTests.csOptionList<T> does) if needed.