Skip to content

Data Widgets

List

Selectable list of items with optional headers, spacers, prefixes, left gutters, and scrollbar.

State-style setters use StyleSlot semantics: selection_style / active_style replace theme roles, while extend_selection_style / extend_active_style patch over them and inherit_selection_style / inherit_active_style delegate to the theme.

PropTypeDescription
itemsimpl Iterator<Item = ListItem>List items
selectedOption<usize>Selected index; pass None for no current row (no selection highlight). Bare integers still work (list.selected(0))
scroll_keysboolEnable keyboard scroll keys
scroll_wheelboolEnable mouse wheel
scrollbarboolShow scrollbar
scrollbar_configScrollbarConfigFull scrollbar configuration (variant, gap, thumb, thumb styles)
show_scroll_indicatorsboolShow top/bottom overflow indicators
scroll_indicator_styleStyleOverflow indicator style
borderboolDraw border
titleStringBorder title
paddingimpl Into<Padding>Inner padding
empty_textStringText when list is empty
empty_text_styleStyleEmpty text style
active_styleStyleStyle for rows where ListItem::active(true)
extend_active_style / inherit_active_styleStyle / ()Extend or inherit the active-row theme role instead of replacing it
active_symbolOption<impl Into<Arc<str>>>Prefix symbol for active rows
active_symbol_positionListSymbolPositionRender active symbol on the left or immediately after the label
active_symbol_styleStyleStyle for active symbol
selection_symbolOption<String>Prefix for selected item (e.g. "> ")
selection_symbol_rightOption<String>Trailing symbol after the selected item's label; pair with selection_symbol for "pill" caps. Shares selection_symbol_style
symbol_columnboolEnable reservation/rendering for the built-in selection/status symbol column
selection_styleStyleSelected item style
extend_selection_style / inherit_selection_styleStyle / ()Extend or inherit the selection theme role instead of replacing it
unfocused_selection_styleStyleSelected item style while list is not focused; defaults to selection_style
extend_unfocused_selection_style / inherit_unfocused_selection_styleStyle / ()Extend or inherit the unfocused selection theme role instead of replacing it
selection_full_widthboolExtend selection to full width
unfocused_selection_symbol_styleStyleSelection symbol style while list is not focused; defaults to selection_symbol_style
gutter_gapu16Cells between row-local gutters and labels; default 0, opt into spacing with .gutter_gap(n)
gutter_for_non_selectableboolInclude non-selectable rows (headers/spacers) in gutter reservation/alignment
item_horizontal_paddingimpl Into<Padding>Left/right padding for normal rows (top/bottom ignored)
header_horizontal_paddingimpl Into<Padding>Left/right padding for header rows (top/bottom ignored)
focusableboolAccept focus
tab_stopboolInclude in sequential Tab traversal (default: true)
on_focus / on_blurCallback<()>Focus gained / lost
widthLengthWidth
heightLengthHeight
on_selectCallback<ListEvent>Selection changed
on_item_clickCallback<ListEvent>Row clicked with mouse
on_activateCallback<ListEvent>Item activated (Enter/double-click)
on_scroll_toCallback<usize>Scroll position changed

ListItem Types

rust
ListItem::new("Normal item")          // Selectable row
ListItem::header("Section Title")      // Non-selectable header row
ListItem::spacer()                     // Non-selectable blank row
ListItem::new("Service").active(true)  // Marks row as active
ListItem::role(ListItemRole::Header)   // Explicit role

// Multi-line rows
ListItem::new("build")
    .line(ListItemLine::new("target/debug/build.log").selection_left(false))

// Prefix helpers
ListItem::new("Item").numbered(1)
ListItem::new("Bullet").bulleted('•')
ListItem::new("Label")
    .prefix("> ")
    .prefix_style(Style::new().fg(Color::Cyan))

// Left gutter helpers. Spinner gutters animate with the app's spinner ticker.
// Set List::gutter_gap(1) when the framework should provide label spacing.
// Use `.leading(1)` so a lone spinner lines up with text markers like `" ●"`.
ListItem::new("Building").gutter(ListItemGutter::spinner(Spinner::new()).leading(1))
ListItem::new("Changed").gutter(ListItemGutter::text("~ "))

// Status helpers render inside the existing selection/unselected symbol column.
ListItem::new("Working").status_spinner(Spinner::new())
ListItem::new("Dirty").status_symbol(" ~ ")

// Symbol/gutter can be rendered on a non-primary line (useful for "description above")
ListItem::new("description")
    .line(ListItemLine::new("label"))
    .symbol_line(1)
    .gutter_line(1)

Keyboard/mouse selection and activation skip non-selectable rows (Header/Spacer).

ListItem::line(...) adds extra visual lines under the primary line. Selection, activation, and callbacks still use the item index (not visual line index).

ListItem::prefix(...) renders a prefix before the primary label and automatically indents extra lines to match the label start. Use .extra_line_indent(...) to override that alignment when needed.

ListItem::gutter(...) is the canonical row-local leading adornment: use it for per-row markers, icons, spinners, or badges that need their own column before the label. Gutters reserve a consistent left gutter column across participating rows so labels stay aligned even when only some rows have gutter content. Text markers usually bake a leading indent into the string (for example " ●"); spinner gutters are only as wide as the frame, so the renderer lead-pads them inside that reserved column to keep the glyph on the same cell as those markers. The default gap between the gutter and label is 0; opt into spacing with .gutter_gap(n). By default, only selectable item rows participate, keeping headers left-aligned; use .gutter_for_non_selectable(true) when headers/spacers should reserve the same gutter width.

ListItem::status(...), .status_symbol(...), and .status_spinner(...) render inside the existing list symbol column. Status is symbol-column content, not a separate row gutter. Active symbols keep priority, then row status, then selected symbols, then the unselected symbol/spaces. Use this for one-column row state such as a busy spinner.

List::symbol_column(true) enables the built-in status/selection symbol column; the reserved width still comes from configured selection/unselected/active symbols or row status content. Use List::symbol_column(false) when row-local gutters provide the leading markers and the built-in column should not measure or render.

ListConfig

Composite widgets such as Select, ComboBox, MultiSelect, and SearchPalette forward shared list chrome through ListConfig. In addition to the convenience setters on those widgets, the struct carries these fields for builder-style or typed configuration:

FieldTypeDescription
borderboolWhether to draw a border around the inner list
border_styleBorderStyleInner list border style
paddingPaddingInner list padding
styleStyleInner list base style
selection_styleStyleSlotSelected/active row style
unfocused_selection_styleStyleSlotSelected row style while the list is unfocused
item_hover_styleOption<StyleSlot>Per-row hover style; None lets the host widget apply its own default (several fall back to selection_style)
selection_full_widthboolExtend selection across the row width
selection_symbolOption<Arc<str>>Selected-row symbol content
selection_symbol_rightOption<Arc<str>>Trailing selected-row symbol (right "pill" cap); shares selection_symbol_style
selection_symbol_styleOption<Style>Selected-row symbol style
unfocused_selection_symbol_styleOption<Style>Selected-row symbol style while unfocused
symbol_columnboolReserve the list symbol/status column
gutter_gapu16Gap between row-local gutters and labels; default 0
gutter_for_non_selectableboolWhether headers/spacers participate in gutter reservation
item_horizontal_paddingPaddingLeft/right padding for normal rows (interior to the highlight)
header_horizontal_paddingPaddingLeft/right padding for header rows
empty_text_styleStyleStyle for the empty-state placeholder text
scrollbarboolShow a vertical scrollbar when content overflows
scrollbar_configScrollbarConfigFull scrollbar configuration

Prefer ListConfig::new()/Default::default() with builder methods over struct literals so newly added fields do not break app code.

selection_symbol (e.g. "> ") is prepended to the selected item. Unselected items are padded with spaces to maintain alignment. Include a trailing space in the symbol if needed.

Pill / capsule selection: pair selection_symbol (left cap) with selection_symbol_right (right cap). Color both caps via selection_symbol_style with foreground equal to the selection background and background equal to the row/terminal background, e.g. selection_symbol(Some("")), selection_symbol_right(Some("")), selection_symbol_style(Style::new().fg(sel_bg).bg(row_bg)). The trailing cap renders only on the selected row and always closes the right edge of the highlighted region. The highlight spans the full row width only when you opt in with selection_full_width(true) (or when right-aligned content must be pushed to the edge); otherwise it hugs the content — item_horizontal_padding stays interior to the highlight and does not force a full-width bar. So selection_full_width(false) gives a tight capsule around the label (plus any padding), and selection_full_width(true) gives a full-width capsule with the cap at the row's right edge. A right-positioned active_symbol shares this slot and takes priority when both apply.

Active row rendering is independent from selection. Use active_symbol_position(ListSymbolPosition::Right) to render the active marker after the label instead of in the left symbol column. When using the right position, include any desired separator in the symbol itself (for example " ✓").

item_horizontal_padding and header_horizontal_padding accept Padding, but only left/right are applied in List.

File icons in a plain list: the Nerd Font icon resolvers FileTree uses are exposed via tui_lipan::utils::{file_icon, file_icon_span, directory_icon, directory_icon_span}, so you can prefix list rows with themed file/folder icons without reimplementing the mapping. Pass a FileIconPalette (e.g. theme.file_icons): ListItem::from_spans([file_icon_span(name, &palette), Span::new(format!(" {name}"))]) for files, or directory_icon_span(expanded, &palette) for folders (bare glyph only; no disclosure arrow).

Pointer vs keyboard row hover: For List and Table, when item_hover_style is non-empty, changing selected from the keyboard or from component logic (not a row click) stops using the mouse position for per-row hover until the pointer moves. Widget-level hover and row clicks behave as usual. Tree uses an inner List with the same item_hover_style prop, so it follows the same rules automatically.

rust
List::new()
    .items(self.files.iter().map(|f| ListItem::new(f.name.clone())))
    .selected(self.selected)
    .scrollbar(true)
    .selection_symbol(Some("> ".to_string()))
    .selection_style(Style::new().fg(Color::Cyan).bold())
    .on_select(ctx.link().callback(|e| Msg::FileSelected(e.index)))
    .on_activate(ctx.link().callback(|e| Msg::FileOpened(e.index)))

Table

Structured data with rows, columns, and optional scrollbar.

PropTypeDescription
headerTableRowColumn header row
rowsArc<[TableRow]> / rows_arcData rows (rows(...) collects into Arc; prefer rows_arc when sharing)
widthsVec<ColumnWidth>Column widths
selectedOption<usize>Selected row index
column_spacingu16Space between columns
row_gapu16Blank terminal rows between rendered table rows
scroll_keysboolKeyboard scroll
scroll_wheelboolMouse wheel
scrollbarboolScrollbar
scrollbar_configScrollbarConfigFull scrollbar configuration (variant, gap, thumb, thumb styles)
show_scroll_indicatorsboolOverflow indicators
scroll_indicator_styleStyleIndicator style
selection_symbolOption<String>Selected row prefix
selection_styleStyleSelected row style
extend_selection_style / inherit_selection_styleStyle / ()Extend or inherit the selection theme role instead of replacing it
header_styleStyleHeader row style
row_styleStyleDefault row style
alternating_row_styleStyleStyle applied to odd rows for zebra striping
column_style(usize, Style)Style a zero-based column; applies to header and data cells
column_stylesimpl IntoIterator<Item = Style>Set zero-based per-column styles in order
row_style_at(usize, Style)Style a zero-based absolute data row; does not affect the header
row_stylesimpl IntoIterator<Item = Style>Set zero-based per-data-row styles in order
row_style_full_widthboolExtend row hover/selection/zebra style across full row width
focusableboolAccept focus
tab_stopboolInclude in sequential Tab traversal (default: true)
on_focus / on_blurCallback<()>Focus gained / lost
widthLengthWidth
heightLengthHeight
on_selectCallback<TableEvent>Row selection changed
on_activateCallback<TableEvent>Row activated
on_scroll_toCallback<usize>Scroll position

Table style precedence for body cells is: alternating row style, then TableRow::style, indexed row style (row_style_at / row_styles), column style (column_style / column_styles), TableCell::style, and finally state styles such as hover/selection/disabled. Header cells use header styling plus column and cell styles; indexed row styles only target data rows. Full-row backgrounds from row_style_full_width(true) use row-level styles, not column or cell styles.

Column Widths

rust
ColumnWidth::Fixed(10)   // Fixed cell width
ColumnWidth::Fill(1)     // Proportional fill
ColumnWidth::Min(5)      // Minimum width, fills remaining

Row Sizing

rust
TableRow::new(vec!["col1", "col2"])
    .height(2)           // Fixed height
    .auto_height()       // Height = max line count of cells
    .bottom_margin(1)    // Spacing below row

Table::new()
    .row_gap(1)          // Global blank rows between rendered rows

Table::row_gap(gap) inserts a global number of blank terminal rows between rendered table rows. It applies between the header and first data row only when data rows exist, and between data rows, but never after the final data row. This global gap is additive with per-row TableRow::bottom_margin: use row_gap for consistent table-wide spacing, and bottom_margin for row-specific extra space.

Heatmap Cells

rust
TableCell::heat_fg(value, &gradient, GradientRange::new(0.0, 100.0))  // Color fg by value
TableCell::heat_bg(value, &gradient, GradientRange::new(0.0, 100.0))  // Color bg by value

Inspector Patterns

rust
Table::inspector(true)   // Enable inspector presets

// Row helpers
TableRow::key_value("Name", "Alice")
TableRow::section("Personal Info")
TableRow::separator()

// Hierarchy
TableRow::new(cells)
    .depth(2)
    .disclosure(TableDisclosureState::Expanded)

Inspector styling hooks: inspector_key_style, inspector_value_style, inspector_section_style, inspector_separator_style, inspector_indent_size, inspector_disclosure_symbols, inspector_separator_char.

Row semantics: TableRowRole::{Normal, Section, Separator}.

rust
Table::new()
    .header(TableRow::new(vec!["ID", "Name", "Status"]))
    .rows(self.data.iter().map(|d| {
        TableRow::new(vec![d.id.to_string(), d.name.clone(), d.status.clone()])
    }).collect())
    .widths(vec![ColumnWidth::Fixed(5), ColumnWidth::Fill(1), ColumnWidth::Fixed(10)])
    .alternating_row_style(Style::new().bg(Color::indexed(236)))
    .row_style_full_width(true)
    .selected(Some(self.selected))
    .selection_style(Style::new().fg(Color::Cyan))
    .on_select(ctx.link().callback(|e| Msg::RowSelected(e.index)))

Tree

Hierarchical tree view with expand/collapse.

PropTypeDescription
rootTreeNodeConstructor - root node
selectedusizeControlled selected visible row index
clear_selectionboolWhen true, suppress the selection highlight (authoritative over selected and internal state)
force_scroll_to_selectedboolForce the internal list to reveal the selected row on next render
gapu16Vertical gap between items
icon_gapu16Gap between icon and label
show_iconsboolShow expand/collapse icons
expanded_iconStringIcon for expanded nodes
collapsed_iconStringIcon for collapsed nodes
leaf_iconStringIcon for leaf nodes
icon_styleStyleIcon style
indent_styleIndentStyleIndent guide glyph variant: None, Line, Short, Long, ShortRounded, or LongRounded
indent_guide_styleStyleVertical indent guide
indent_guide_start_depthusizeFirst non-root depth that renders guides (default: 1)
indent_gradientColorGradientGradient for indent depth
styleStyleBase style
hover_styleStyleHover style
extend_hover_style / inherit_hover_styleStyle / ()Extend or inherit the hover theme role instead of replacing it
item_hover_styleStyleIndividual item hover
extend_item_hover_style / inherit_item_hover_styleStyle / ()Extend or inherit the item hover theme role instead of replacing it
selection_styleStyleSelected item style
extend_selection_style / inherit_selection_styleStyle / ()Extend or inherit the selection theme role instead of replacing it
unfocused_selection_styleStyleSelected item style while tree is not focused; defaults to selection_style
extend_unfocused_selection_style / inherit_unfocused_selection_styleStyle / ()Extend or inherit the unfocused selection theme role instead of replacing it
selection_symbolStringSelected item prefix
selection_symbol_styleStylePrefix style
unfocused_selection_symbol_styleStylePrefix style while tree is not focused; defaults to selection_symbol_style
scrollbarboolScrollbar
scrollbar_configScrollbarConfigFull scrollbar configuration (variant, gap, thumb, thumb styles)
scroll_keysboolKeyboard scroll
scroll_wheelboolMouse wheel
empty_textStringText when tree is empty
empty_text_styleStyleEmpty text style
focusableboolAccept focus
tab_stopboolInclude in sequential Tab traversal (default: true)
on_focus / on_blurCallback<()>Focus gained / lost
activate_on_clickboolSingle-click activates
keymapTreeKeymapKeyboard expand/collapse mapping
focus_policyFocusAccordionTree accordion behavior
focus_keyStringStable key assigned to the inner focusable list node
widthLengthWidth
heightLengthHeight
on_selectCallback<TreeEvent>Node selected
on_toggleCallback<TreeToggleEvent>Node expanded/collapsed

Tree is implemented with an inner List (flattened visible rows). Per-row item_hover_style and pointer vs keyboard row hover behavior match List - see the callout under List.

IndentStyle controls connector glyphs: None disables guides, Line uses , Short uses /, Long uses ├─/└─, ShortRounded uses /, and LongRounded uses ├─/╰─.

Building Nodes

rust
TreeNode::new("parent")
    .expanded(true)
    .child(
        TreeNode::new("child-1")
    )
    .child(
        TreeNode::new("child-2")
            .child(TreeNode::new("grandchild"))
    )

// With styled ListItem
TreeNode::new(ListItem::from_spans(vec![
    Span::new("file.rs").fg(Color::Cyan),
    Span::new(" [modified]").fg(Color::Yellow),
]))

Use TreeNode::expandable(true) when a node must retain expand/collapse behavior while its current child list is empty, such as an asynchronously loaded directory.

Events

rust
// TreeEvent { index: usize, path: Vec<usize> }
// TreeToggleEvent { index: usize, path: Vec<usize>, expanded: bool }

.on_select(ctx.link().callback(|e: TreeEvent| Msg::NodeSelected(e.path)))
.on_toggle(ctx.link().callback(|e: TreeToggleEvent| Msg::NodeToggled(e.path, e.expanded)))

Keymap: TreeKeymap supports expand/collapse via Left/Right, h/l, Space (toggle).


FileTree

Lazy-loading filesystem explorer built on Tree, with local or application-provided directory entries and git-backed or application-provided change projections.

PropTypeDescription
rootimpl Into<Arc<str>>Constructor - root directory path
entry_sourceFileTreeEntrySourceDirectory entry source; defaults to local filesystem enumeration
entry_refresh_tokenu64Re-read local root and currently expanded directories when the token changes; ignored for provided entries
show_hiddenboolShow hidden files (. prefix)
max_entries_per_dirusizeCap entries per directory
directory_label_styleStyleStyle applied to directory names
file_label_styleStyleStyle applied to regular file names
path_style(path, FileTreeItemStyle)Apply row/icon/label/suffix styles to one exact path
path_stylesimpl IntoIterator<Item = (path, FileTreeItemStyle)>Apply exact path-specific item styles in bulk
git_statusboolShow change status badges for git/provided changes (default: true)
highlight_changed_labelsboolAlso apply change status colors to file/directory labels (default: false)
change_suffix_styleStyleStyle only the right-side change metadata suffix, such as status markers and diff stats
change_suffix_priorityFileTreeSuffixPriorityWhether labels or right-side change metadata are preserved first when rows are narrow
change_sourceFileTreeChangeSourceChange metadata source; defaults to local git status/diff data
change_viewFileTreeChangeViewAllFiles (default) or ChangedOnly source-agnostic view
show_diff_statsboolShow +N -M diff stats next to change markers
git_suffix_styleStyleCompatibility setter for styling only right-side git metadata
git_suffix_priorityFileTreeSuffixPriorityCompatibility setter for right-side git metadata truncation priority
git_viewFileTreeGitViewAllFiles (default) or ChangedOnly git-focused view
git_changed_onlyboolCompatibility convenience setter for changed-only mode
git_diff_statsboolCompatibility setter for +N -M diff stats next to change markers
git_status_cacheFileTreeGitStatusCacheApp-owned bounded cache that keeps the last successful local Git snapshot visible across mounts while it revalidates
git_refresh_tokenu64Token to trigger deterministic git refresh
selectedusizeControlled selected visible row index
clear_selectionboolWhen true, suppress the selection highlight (authoritative over selected and internal state)
selected_pathimpl Into<Arc<str>>Controlled selection by absolute path under the root or path relative to the root, when the row is visible
reveal_pathimpl Into<Arc<str>>Expand/load ancestors for an absolute or root-relative path when possible
select_pathimpl Into<Arc<str>>Reveal and select a path, forcing the tree to scroll to the row when visible
force_scroll_to_selectedboolForce the tree to reveal the selected row on next render
expanded_pathsimpl IntoIterator<Item = impl Into<Arc<str>>>Controlled expanded directory paths; the root path is kept expanded automatically
initial_expanded_pathsimpl IntoIterator<Item = impl Into<Arc<str>>>Expansion seeded on mount and on each root change, which the tree then owns
empty_text_paddingimpl Into<Padding>Inset around the empty-state text; defaults to none, flush with the rows
indent_widthu16Indentation cells per hierarchy level (default: 2); use 1 with short guides for compact ├item rows
directory_iconStringDirectory icon
file_iconStringFile icon
symlink_iconStringSymlink icon
other_iconStringOther entry icon
explorerboolShow fuzzy search input
tree_focus_keyStringKey used for direct focus of the inner tree
explorer_focus_keyStringKey used for direct focus of the explorer input
explorer_placeholderStringSearch input placeholder
explorer_prefixStringSearch input prefix
explorer_input_borderboolSearch input border
explorer_match_styleStyleFuzzy match highlight
explorer_dividerboolShow divider between search and tree
on_explorer_focusCallback<FileTreeExplorerFocusOrigin>Explorer input focused from the tree or pointer
on_explorer_blurCallback<()>Explorer input lost focus
on_explorer_escapeCallback<()>Leave pointer-focused explorer input on Escape or an outside tree/divider click
focusableboolAccept focus
tab_stopboolInclude the tree target in sequential Tab traversal (default: true)
on_focus / on_blurCallback<()>Tree focus gained / lost
on_selectCallback<FileTreeEvent>File/dir selected
on_toggleCallback<FileTreeToggleEvent>Directory expanded/collapsed
on_entry_requestCallback<FileTreeEntryRequest>Missing provided directory listing requested

Plus all Tree styling/scrolling props, including indent_style and scrollbar_config.

Behavior:

  • Local directories load on demand on first expand.
  • Change entry_refresh_token(...) to refresh local directory entries in place without remounting or resetting expansion or explorer state. The root and currently expanded directories are reread; collapsed descendants are unloaded and reread when reopened. Reads run in a background command; results for an older token, root, or entry source are ignored. An active explorer query reruns against the refreshed entries, and uncontrolled selection follows its path with the root as the deterministic fallback when that path disappears. This does not refresh Git status; use git_refresh_token(...) separately. Provided entry sources ignore the token because their listings are owned by the application.
  • Every directory is expandable, including one that turns out to be empty; a lazy tree cannot know a directory is empty until it has been opened, so its row keeps the same shape either way.
  • empty_text renders in place of the tree when a ChangedOnly view has nothing changed. Browsing keeps its root row instead, since that row is the directory being browsed rather than a heading over a projection. With FileTreeChangeSource::Git the placeholder waits for the first git status to finish; with provided change data it appears as soon as the supplied list is empty, so an application that fetches its own changes should vary empty_text while it loads.
  • With FileTreeEntrySource::Provided, an absent directory listing is pending: the widget renders loading_label, emits one FileTreeEntryRequest for the expanded path, and waits for the app to rebuild it with a matching FileTreeDirectoryListing. The callback must enqueue remote or other blocking work through a Command; it must not perform that work on the UI thread.
  • Keep completed listings in app state and batch responses received together into one state update. The widget incrementally applies changed directory results, while one batched prop update avoids repeated comparison and status-projection work across a large listing set.
  • Provided entries include is_dir, is_symlink, git_status, and ignored. Their Git status is rendered without local Git discovery. Ignored entries remain visible during normal browsing, as with the local source, but explorer search excludes them.
  • In changed-only mode, provided directories with pending descendants remain visible so they can be expanded and requested; a loaded clean directory disappears from that projection.
  • Git is the default change source; use FileTreeChangeSource::Provided(...) to display backend-provided change data without requiring a local git repository.
  • Clone one FileTreeGitStatusCache into trees that replace one another to avoid blank Git indicators after a keyed remount. The cache retains successful local snapshots only, separates status-only and diff-stat data, ignores provided sources, and revalidates every mount.
  • directory_label_style(...) and file_label_style(...) style names independently from icons and right-aligned change indicators.
  • path_style(...) / path_styles(...) match exact paths and can override row, icon, label, and suffix styling for reviewed, pinned, or otherwise annotated files.
  • change_suffix_style(...) and git_suffix_style(...) style only right-side metadata such as M +30 -21, leaving icons and labels unchanged.
  • change_suffix_priority(FileTreeSuffixPriority::Suffix) keeps right-side metadata visible first on narrow rows, truncating labels before suffixes.
  • Change colors apply to the right-aligned indicators by default; use highlight_changed_labels(true) to also color dirty file and directory names, layered over their label styles.
  • FileTreeChangeView::ChangedOnly shows changed files only, grouped by ancestor directories. With provided change data this projection is virtual/source-agnostic and can include nonexistent or deleted paths supplied by the backend.
  • FileTreeGitView is a compatibility alias for FileTreeChangeView; git_view, git_changed_only, and git_diff_stats remain available for git-focused call sites.
  • Changed-only mode respects show_hidden; paths under hidden components such as .github/ appear only when hidden entries are enabled.
  • show_diff_stats(true) displays numeric diff stats when the selected change source provides them; untracked and binary files may show a status marker without +N -M counts.
  • Filesystem fuzzy matching respects .gitignore/.ignore rules.
  • In changed-only mode, fuzzy matching is scoped to the changed-path projection instead of the whole filesystem.
  • Auto-expands ancestor directories to reveal search matches.
  • Clicking the explorer input focuses it. Enter commits the query and focuses the tree; Escape entered from the tree with / also returns to the tree. Pointer-entered Escape and outside tree or divider clicks emit on_explorer_escape when configured.
  • on_explorer_focus reports FileTreeExplorerFocusOrigin::Tree or Pointer, and on_explorer_blur reports when the input releases focus for app-level key routing.
  • selected_path, reveal_path, and select_path normalize absolute paths under the root or paths relative to the root. They are no-ops for paths outside the root, paths hidden by show_hidden(false), absent/unreadable/capped entries, or rows filtered out by the current all-files/changed-only projection. selected_path only selects an already-visible row; reveal_path expands/loads ancestors when possible; select_path combines reveal + selection and scrolls to the selected row. With controlled expanded_paths, app-provided expansion remains authoritative, so reveal/select can only display rows made available by the controlled expansion set plus the reveal request during rendering.
  • initial_expanded_paths restores expansion across an unmount or a root change: it seeds the tree's own expansion state on mount and on every root change, and is never applied again, so it cannot fight expansion the user has changed since. Record the set with on_toggle. Paths outside the root are ignored, and ancestors are not expanded alongside a seeded path — collapsing a directory leaves what is inside it expanded, so a seeded descendant must not reopen the parent that hides it. Ignored when controlled expanded_paths is set.
  • Restores pre-search expansion state when query clears.
  • Queries containing file extensions (e.g. layout.rs) prioritize filename matches.

Run the complete asynchronous pattern with cargo run --example provided_file_tree.

rust
let listings = vec![FileTreeDirectoryListing::new(
    project_root.clone(),
    [
        FileTreeEntry::directory("src"),
        FileTreeEntry::file("README.md").git_status(GitFileStatus::new(
            None,
            Some(GitChangeState::Modified),
        )),
    ],
)];

FileTree::new(project_root)
    .entry_source(FileTreeEntrySource::Provided(listings))
    .on_entry_request(
        ctx.link()
            .callback(|request: FileTreeEntryRequest| Msg::ListDirectory(request.path)),
    )
rust
let changes = vec![
    FileTreeChange::new("src/main.rs", FileTreeChangeStatus::Modified)
        .kind(FileKind::File)
        .diff_stat(12, 3)
        .staged(true),
    FileTreeChange::new("docs/removed.md", FileTreeChangeStatus::Deleted)
        .kind(FileKind::File),
];

FileTree::new(project_root)
    .change_source(FileTreeChangeSource::Provided(changes))
    .change_view(FileTreeChangeView::ChangedOnly)
    .show_diff_stats(true)
    .path_style(
        "src/main.rs",
        FileTreeItemStyle::new()
            .row(Style::new().fg(Color::DarkGray))
            .icon(Style::new().fg(Color::DarkGray))
            .label(Style::new().fg(Color::DarkGray)),
    )
    .change_suffix_style(Style::new().fg(Color::Yellow))
    .change_suffix_priority(FileTreeSuffixPriority::Suffix)
rust
FileTree::new("/home/user/projects")
    .git_status(true)
    .change_view(FileTreeChangeView::ChangedOnly)
    .show_diff_stats(true)
    .show_hidden(false)
    .explorer(true)
    .explorer_placeholder("Filter files...")
    .on_select(ctx.link().callback(|e: FileTreeEvent| Msg::FileSelected(e.path)))

Events

rust
// FileTreeEvent { path: Arc<str>, kind: FileKind }
// FileTreeToggleEvent { path: Arc<str>, kind: FileKind, expanded: bool }
// FileTreeEntryRequest { path: Arc<str> }

LogView

High-throughput log list with level highlighting and fuzzy filtering.

PropTypeDescription
bufferArc<LogBuffer>Bounded ring buffer of log entries
filter_modeMatchModeFuzzy, Substring, Exact
case_sensitiveboolCase-sensitive filtering
auto_followboolAuto-scroll to newest entry
pausedboolPause log streaming display
trace_styleStyleTRACE level style
debug_styleStyleDEBUG level style
info_styleStyleINFO level style
warn_styleStyleWARN level style
error_styleStyleERROR level style
unfocused_selection_styleStyleSelected row style while log view is not focused; defaults to selection_style
extend_unfocused_selection_style / inherit_unfocused_selection_styleStyle / ()Extend or inherit the unfocused selection theme role instead of replacing it
on_selectCallback<LogViewEvent>Entry selected
on_activateCallback<LogViewEvent>Entry activated

Plus standard list/scroll styling props, including scrollbar_config.

rust
let buffer = Arc::new(LogBuffer::new(10_000)); // 10k entry ring buffer

// In a background thread: buffer.push(LogEntry { level, message });

LogView::new(buffer.clone())
    .filter_mode(MatchMode::Fuzzy)
    .auto_follow(true)
    .info_style(Style::new().fg(Color::Green))
    .error_style(Style::new().fg(Color::Red).bold())

Events

rust
// LogViewEvent { visible_index: usize, source_index: usize, entry: LogEntry }

MPL-2.0