List
List is a focusable, theme-aware list component. The focused row lives in app state as an index; optional selected rows also live in app state. Moving with Up and Down emits the next focused index. ListWidget is the paint-only layer.
Focus the demo, use Up and Down to move focus, press Enter to select the focused item, and press Alt+T to choose a bundled theme.
Interactive Component
use ratcn::List;
use ratcn::view::View;
const ITEMS: [&str; 3] = ["Inbox", "Archive", "Settings"];
struct AppState {
focus: FocusState,
theme: Theme,
focused_item: usize,
selected_item: Option<usize>,
}
enum Msg {
FocusChanged(FocusState),
FocusedItemChanged(usize),
ItemSelected(usize),
}
View::new("root")
.focus(|s: &AppState| &s.focus, Msg::FocusChanged)
.theme(|s: &AppState| &s.theme)
.child(
"list",
List::new(ITEMS)
.focused_row(|s: &AppState| s.focused_item)
.selected(|s: &AppState| s.selected_item)
.on_focus_change(Msg::FocusedItemChanged)
.on_select(Msg::ItemSelected),
)
.content(|frame, _state, view| view.render_child(frame, "list", area));List API
List::new(items): Creates a list from strings or string-like values..focused_row(|state| index): Reads the focused row index from app state. Out-of-range indexes clamp to the last item..selected(|state| Option<usize>): Reads a single selected row from app state. Out-of-range indexes are ignored..selected_many(|state, index| bool): Reads multi-selection membership from app state..on_focus_change(Msg::Ctor): Emits the next focused index when Up or Down moves..on_change(Msg::Ctor): Backwards-compatible alias for.on_focus_change(...)..on_select(Msg::Ctor): Emits the current focused index when Enter is pressed..render_item(|state, row| Line::from(...)): Customizes row rendering withListItemState..focus_symbol(symbol): Sets the symbol shown before the focused row. Pass""to hide it..disabled(|state| ...): Renders disabled, ignores events, and leaves Tab traversal while true.
List is focusable only when it has at least one item and is not disabled.
Events
Up moves to the previous item. Down moves to the next item. Enter selects the current item. Modified keys are ignored, so app-level shortcuts can use Ctrl, Alt, or Shift combinations without conflicting with the list.
If .on_focus_change(...) is omitted, Up and Down are consumed but no message is emitted. If .on_select(...) is omitted, Enter is consumed but no message is emitted. Disabled and empty lists ignore events.
Custom Rows
Use .render_item(...) when the default marker/text row is not enough. The closure receives ListItemState with index, item, focused, selected, and disabled fields.
List::new(ITEMS)
.focused_row(|s: &AppState| s.focused_item)
.selected_many(|s: &AppState, index| s.selected_items.contains(&index))
.on_focus_change(Msg::FocusedItemChanged)
.on_select(Msg::ToggleItem)
.focus_symbol("")
.render_item(|_state, row| Line::from(format!("{} {}", row.index + 1, row.item)));Paint Widget
Use ListWidget when you only need rendering.
use ratatui::text::Line;
use ratcn::ListWidget;
let items = vec![Line::from("Inbox"), Line::from("Archive")];
let selected_rows = [1];
frame.render_widget(
ListWidget::new(&items)
.focused_row(Some(focused_item))
.selected_rows(&selected_rows)
.focused(contains_keyboard_focus)
.themed(&theme),
area,
);| Method | Description |
|---|---|
ListWidget::new(&items) | Creates a paint-only list over &[Line<'static>]. |
.focused_row(Some(index)) | Marks the focused row. |
.focused_row(None) | Renders without a focused row. |
.selected_rows(&indexes) | Highlights selected rows. |
.focused(bool) | Controls the focused-row symbol. |
.is_focused() | Returns whether focused styling is enabled. |
.disabled(bool) | Draws disabled styling. |
.is_disabled() | Returns whether disabled styling is enabled. |
.themed(&theme) | Applies theme colors. |
.style(ListStyle) | Uses an explicit style. |
.focus_symbol(symbol) | Sets the focused-row symbol. |