Skip to content

Input Widgets

State-style setters in this file use StyleSlot semantics: hover_style, focus_style, selection_style, and prefixed variants replace theme roles by default; matching extend_*_style setters patch over the scoped theme role and inherit_*_style setters delegate to it.

Button

Interactive button.

PropTypeDescription
labelimpl Into<String>Constructor - button text
variantButtonVariantVisual variant
styleStyleIdle style
hover_styleStyleHover style
extend_hover_style / inherit_hover_styleStyle / ()Extend or inherit the hover theme role instead of replacing it
focus_styleStyleFocus style
extend_focus_style / inherit_focus_styleStyle / ()Extend or inherit the focus theme role instead of replacing it
border_styleBorderStyleIdle border
hover_border_styleBorderStyleHover border
focus_border_styleBorderStyleFocus border
alignAlignLabel alignment
paddingimpl Into<Padding>Inner padding
iconSpanIcon span (prepended to label)
icon_styleStyleIcon style
icon_gapu16Space between icon and label
shortcutStringKeyboard shortcut label
shortcut_bindingsKeyBindingsKeyboard shortcut alternatives (displayed canonically)
shortcut_styleStyleShortcut style
shortcut_gapu16Space between label and shortcut
widthLengthWidth
heightLengthHeight
focusableboolWhether button accepts focus
disabledboolDisable button
disabled_styleStyleStyle when disabled
on_clickCallback<MouseEvent>Mouse click, plain Enter, or plain Space activation callback
on_keyKeyHandlerKey handler that can intercept focused keys before activation

Focused buttons invoke on_click for mouse activation and for unmodified Enter / Space. A custom on_key handler runs first; return true there to consume the key without also firing on_click.

rust
Button::new("Save")
    .style(Style::new().fg(Color::White).bg(Color::Blue))
    .shortcut_bindings("ctrl+s, super+s".parse().unwrap())
    .focus_style(Style::new().fg(Color::White).bg(Color::DarkBlue).bold())
    .on_click(ctx.link().callback(|_| Msg::Save))

DragSource

Wrapper that turns a single child into a generic drag source.

PropTypeDescription
childimpl Into<Element>Wrapped draggable content
on_drag_startFn(DragStartEvent) -> Option<Box<dyn DragPayload>>Drag-start handler that returns payload; None aborts drag
on_drag_cancelCallback<DragCancelEvent>Fired when drag is canceled or dropped on invalid target
on_drag_startedCallback<DragStartedEvent>Fired once when the drag activates (after the movement threshold); includes payload
drag_groupimpl Into<Arc<str>>Optional compatibility group
clear_drag_group()Remove group restriction
previewDragPreviewLabel text near pointer, SourceSnapshot (layout slot collapses per drag_slot; float preview copies cells), or None
preview_labelimpl Into<Arc<str>>Convenience for DragPreview::Label
preview_snapshot()Convenience for DragPreview::SourceSnapshot
no_preview()Convenience for DragPreview::None
drag_slotDragSlotCollapse (0 main-axis cells) or Specified(Length) using the same Length rules as stack children (Auto, Px, Percent, Flex). In VStack/HStack the stack axis applies; elsewhere use drag_slot_axis.
drag_slot_collapse()Same as drag_slot(DragSlot::Collapse)
drag_slot_lengthLengthSame as drag_slot(DragSlot::Specified(len))
drag_slot_axisDragSlotAxisVertical or Horizontal: which axis Fixed/Collapse apply to when measured outside a stack (default: vertical)
dragging_styleStyleOverlay while dragging: first-frame tint, reserved slot fill when using SourceSnapshot, and label/none preview modes
extend_dragging_style / inherit_dragging_styleStyle / ()Extend or inherit the drag-source theme role for the dragging overlay
preview_max_widthOption<u16>Max width of the floating SourceSnapshot preview (NoneDEFAULT_PREVIEW_MAX_WIDTH)
preview_max_heightOption<u16>Max height of the floating preview (NoneDEFAULT_PREVIEW_MAX_HEIGHT)
preview_max_size(Option<u16>, Option<u16>)Set both max dimensions
thresholdu16Pointer movement threshold before drag starts (default: 3)
enabledboolEnable/disable drag behavior
rust
DragSource::new()
    .child(Text::new("main.rs"))
    .on_drag_start(|ev| {
        let _ = (ev.x, ev.y);
        Some(Box::new(String::from("main.rs")) as Box<dyn DragPayload>)
    })
    .preview_label("main.rs")
    .threshold(3)

DropTarget

Wrapper that marks a single child as a generic drop zone.

PropTypeDescription
childimpl Into<Element>Wrapped drop-zone content
on_drag_overCallback<DragOverEvent>Fired when compatible payload hovers target
on_drag_leaveCallback<DragLeaveEvent>Fired when active drag leaves target
on_dropCallback<DropEvent>Fired on successful drop
accept_groupimpl Into<Arc<str>>Restrict accepted source group
clear_accept_group()Accept all groups (None)
can_acceptPayloadAcceptFnOptional payload predicate
can_accept_withFn(&dyn DragPayload) -> boolClosure form of payload predicate
clear_can_accept()Remove payload predicate
highlightDropHighlightNone, Fill, Placeholder (bordered frame), or Overlay (tint after children)
highlight_styleStyleStyle for Fill / Placeholder / Overlay
extend_highlight_style / inherit_highlight_styleStyle / ()Extend or inherit the active drop-target theme role for the drop highlight
highlight_fillStylehighlight(DropHighlight::Fill) + highlight_style
highlight_placeholderStylehighlight(DropHighlight::Placeholder) + highlight_style
highlight_overlayStylehighlight(DropHighlight::Overlay) + highlight_style
drop_slotDropSlotChild (render child normally, default) or SourcePreview (replace child with the dragged card's snapshot; cursor float is suppressed)
drop_slot_source_preview()Shorthand for drop_slot(DropSlot::SourcePreview)
enabledboolEnable/disable drop behavior

on_drag_over is emitted on every pointer move while a compatible drag hovers this target (not only on enter). Use DragOverEvent::local_y (offset from the drop target’s top edge) with a row-height estimate to place a single insertion line. DropEvent::local_y uses the same convention.

rust
DropTarget::new()
    .child(Text::new("Drop here"))
    .accept_group("files")
    .can_accept_with(|payload| payload.downcast_ref::<String>().is_some())
    .on_drop(ctx.link().callback(|ev| Msg::Dropped(ev.payload)))

MouseRegion

Wrapper that adds pointer callbacks to an arbitrary child subtree.

PropTypeDescription
childimpl Into<Element>Wrapped content
on_clickCallback<MouseEvent>Left-button click after press and release on the same region
on_mouse_downCallback<MouseEvent>Left-button press
on_mouse_upCallback<MouseEvent>Left-button release over the region
on_mouse_moveCallback<MouseMoveEvent>Pointer movement over the region
on_drag_startCallback<MouseDragEvent>First left-button drag tick after the click threshold is exceeded
on_dragCallback<MouseDragEvent>Every left-button drag tick after drag start
on_drag_endCallback<MouseDragEvent>Left-button release after a drag started
drag_requires_modsKeyModsRequire modifiers before left-button drag callbacks can start
on_right_drag_startCallback<MouseDragEvent>First right-button drag tick after the click threshold is exceeded
on_right_dragCallback<MouseDragEvent>Every right-button drag tick after drag start
on_right_drag_endCallback<MouseDragEvent>Right-button release after a drag started
right_drag_requires_modsKeyModsRequire modifiers before right-button drag callbacks can start
bubble_mouse_downboolAlso emit on_mouse_down for descendant presses without consuming them
capture_clickboolCapture clicks over interactive descendants
capture_requires_modsKeyModsCapture pointer handling over descendants while modifiers are held
hover_styleStyleStyle underlay while hovered
enabledboolEnable/disable pointer behavior

Drag callbacks use the same click-cancel threshold as built-in draggable widgets, so a single click does not emit drag-start, drag, or drag-end callbacks. MouseDragEvent includes the global and local drag origin, current global and local coordinates, delta since the previous drag tick, target size, and modifiers. Modifier requirements are subset checks: KeyMods::ALT means Alt must be held, while unrelated extra modifiers are allowed. Use capture_requires_mods(KeyMods::ALT) with modifier-gated wrapper gestures around terminals or text widgets so Alt-click/Alt-drag does not start child selection or forward a terminal mouse report.

rust
MouseRegion::new()
    .on_drag_start(ctx.link().callback(|ev: MouseDragEvent| Msg::Begin(ev.local_x, ev.local_y)))
    .on_drag(ctx.link().callback(|ev: MouseDragEvent| Msg::Draw(ev.local_x, ev.local_y)))
    .on_drag_end(ctx.link().callback(|_| Msg::End))
    .child(AsciiCanvas::blank(40, 12))

Clickable text link built on top of Button with link-style defaults.

PropTypeDescription
labelimpl Into<Arc<str>>Constructor - visible link text
hrefArc<str>Optional URL metadata emitted in callbacks
styleStyleIdle style
hover_styleStyleHover style
extend_hover_style / inherit_hover_styleStyle / ()Extend or inherit the hover theme role instead of replacing it
focus_styleStyleFocus style
extend_focus_style / inherit_focus_styleStyle / ()Extend or inherit the focus theme role instead of replacing it
disabled_styleStyleStyle when disabled
visited_styleStyleStyle overlay when visited: true
alignAlignLabel alignment
paddingimpl Into<Padding>Inner padding
widthLengthWidth
heightLengthHeight
focusableboolWhether link accepts focus
disabledboolDisable interaction
visitedboolMark link as visited
on_activateCallback<HyperlinkEvent>Emits on click, Enter, and Space
on_keyKeyHandlerFallback key handler
rust
Hyperlink::new("Open docs")
    .href("https://example.com/docs")
    .visited(self.docs_opened)
    .visited_style(Style::new().fg(Color::Magenta).underline())
    .on_activate(ctx.link().callback(Msg::OpenLink))

HyperlinkEvent contains:

  • label: Arc<str>
  • href: Option<Arc<str>>

Open the destination explicitly from component logic:

rust
if let Some(url) = ev.href.as_deref() {
    let _ = tui_lipan::utils::open_url(url);
}

Input

Single-line text input field.

State binding: Use Input::bound(&state) to create an Input that reads value, cursor, and anchor from a TextInput state bundle, and InputEvent::apply_to(&mut state) in your update handler to write changes back. This preserves cursor position and selection across rerenders. Using Input::new(value) resets the cursor to the end on every render.

PropTypeDescription
(constructor)impl Into<Arc<str>>Input::new(value) — current text (cursor at end)
(constructor)&TextInputInput::bound(state) — bind value, cursor, and anchor from state
.bind(state)&TextInputApply cursor, anchor, and value from state onto an existing Input
valueimpl Into<Arc<str>>Current text value (use bound/bind instead for cursor preservation)
cursorusizeByte cursor position
anchorOption<usize>Selection anchor (for text selection)
caret_shapeCaretShapeCursor shape - default: Block (only set when you want Bar or Underline)
caret_colorOption<Color>OSC 12 cursor color (terminal support required)
styleStyleIdle style
hover_styleStyleHover style
extend_hover_style / inherit_hover_styleStyle / ()Extend or inherit the hover theme role instead of replacing it
focus_styleStyleFocused style
extend_focus_style / inherit_focus_styleStyle / ()Extend or inherit the focus theme role instead of replacing it
selection_styleStyleText selection style
extend_selection_style / inherit_selection_styleStyle / ()Extend or inherit the text-selection theme role instead of replacing it
placeholderStringPlaceholder when empty
maskOption<char>Masking character (e.g., '*' for passwords)
read_onlyboolAllow selection but block keyboard input
focusableboolWhether input accepts focus; mouse selection and copy shortcuts still work when false
widthLengthWidth
heightLengthHeight
on_changeCallback<InputEvent>Emits value, cursor, and anchor on each edit
on_editCallback<TextEditEvent>Emits structured edit events
key_interceptorKeyHandlerRuns before text insertion

Recommended pattern — store a TextInput in component state and use Input::bound:

rust
struct State {
    query: TextInput,
}

fn create_state(&self, _props: &Self::Properties) -> Self::State {
    State { query: TextInput::new("") }
}

fn view(&self, ctx: &Context<Self>) -> Element {
    Input::bound(&ctx.state.query)
        .placeholder("Search...")
        .style(Style::new().fg(Color::White))
        .focus_style(Style::new().fg(Color::White).bg(Color::indexed(237)))
        .on_change(ctx.link().callback(Msg::QueryChanged))
}

fn update(&mut self, msg: Msg, ctx: &mut Context<Self>) -> Update {
    match msg {
        Msg::QueryChanged(ev) => {
            ev.apply_to(&mut ctx.state.query);
            Update::full()
        }
    }
}

Legacy pattern (cursor resets to end on each render — avoid for editable inputs):

rust
Input::new(self.query.clone())
    .placeholder("Search...")
    .on_change(ctx.link().callback(Msg::QueryChanged))

caret_color uses OSC 12. Set TUI_LIPAN_OSC12=0 to disable if your terminal doesn't support it.

Undo/Redo: Ctrl+Z, Ctrl+Shift+Z, Ctrl+Y (also handles raw control codes).


TextArea

Multi-line text editor.

State binding: Use TextArea::bound(&state) to create a TextArea that reads value, cursor, and anchor from a TextEditor state bundle, and TextAreaEvent::apply_to(&mut state) in your update handler to write changes back. This preserves cursor position and selection across rerenders. Using TextArea::new(value) resets the cursor on every render.

PropTypeDescription
(constructor)impl Into<Arc<str>>TextArea::new(value) — current text
(constructor)&TextEditorTextArea::bound(state) — bind value, cursor, and anchor from state
.bind(state)&TextEditorApply cursor, anchor, and value from state onto an existing TextArea
valueimpl Into<Arc<str>>Current text value (use bound/bind instead for cursor preservation)
cursorusizeByte cursor position
anchorOption<usize>Selection anchor
caret_shapeCaretShapeCursor shape - default: Block; Vim-enabled TextAreas use mode-aware defaults unless overridden
caret_colorOption<Color>OSC 12 cursor color
line_numbersboolShow line number gutter
line_number_modeTextAreaLineNumberModeAbsolute by default; Relative shows Vim-style distances from the cursor line while keeping the cursor line absolute
gutter_insetu16Empty cells before the gutter / line numbers
wrapboolWord wrap (default: true)
max_widthOption<u16>Max line width before forced wrap
scroll_offsetOption<usize>Controlled vertical scroll
scroll_to_lineOption<usize>Zero-based logical/source line target; resolves through wrapped visual rows
scroll_behaviorScrollBehaviorInstant by default; opt into smooth scroll_to_line movement
scroll_transitionTransitionConfigShortcut for smooth line-target movement
scroll_wheelboolMouse wheel scrolling
scroll_wheel_multiplieru16Override the app-wide wheel line multiplier for this TextArea
scrollbarboolVertical scrollbar
scrollbar_configScrollbarConfigFull scrollbar configuration (variant, gap, thumb, thumb styles)
h_scrollbarboolHorizontal scrollbar (only when wrap: false)
styleStyleIdle style
focus_styleStyleFocused style
selection_styleStyleText selection style
extend_selection_style / inherit_selection_styleStyle / ()Extend or inherit the text-selection theme role instead of replacing it
show_selection_when_unfocusedboolKeep the active anchor/cursor selection visible after focus leaves the TextArea (true by default; pass false to opt out)
unfocused_selection_style / inherit_unfocused_selection_style / unfocused_selection_style_slotStyle / () / StyleSlotStyle visible selections while unfocused; inherited/default slots resolve against the text-selection theme role; slot form is for composite forwarding
placeholderStringPlaceholder when empty
placeholder_styleStylePlaceholder style
focus_placeholder_styleStylePlaceholder when focused
read_onlyboolAllow selection but block keyboard input
triple_click_modeTripleClickSelectionModeTriple-click selects a line or paragraph
newline_bindingTextAreaNewlineBindingEnter key behavior
clear_bindingsKeyBindingsPer-widget clear shortcuts; clear is an internal undoable replace edit
vim_motionsboolEnable TextArea-only Vim-style Insert/Normal/Visual/VisualLine motion mode (default: false)
vim_keymapTextAreaVimKeymapWidget-local Vim key remaps to canonical Vim command characters
vim_configTextAreaVimConfigVim-only rendering options: search bar, search match/current-match styles, current-line highlighting, and current line-number/gutter styling
on_changeCallback<TextAreaEvent>Emits value, cursor, anchor on each edit
on_editCallback<TextEditEvent>Structured edit events
on_vim_mode_changeCallback<TextAreaVimMode>Emits Insert/Normal/Visual/VisualLine transitions when vim_motions(true) is enabled
sentinelsVec<TextAreaSentinel>Custom inline PUA tokens (SENTINEL_BASE + index in value)
on_sentinels_changeCallback<Vec<TextAreaSentinel>>Fires when the list is pruned (e.g. user deleted a token)
on_sentinel_eventCallback<Vec<SentinelEvent>>Lifecycle events with stable ids (e.g. Deleted)
on_sentinel_clickCallback<TextAreaSentinelClickEvent>Fires when an inline image or custom sentinel placeholder is clicked
decorationsVec<TextAreaDecoration>Byte-range overlays for ranges, whole lines, and underline markers
virtual_textsVec<TextAreaVirtualText>Non-editable inline inlay hints and EOL diagnostics
gutterTextAreaGutterCompose line numbers, signs, and custom gutter columns
key_interceptorKeyHandlerRuns before text editing
on_scrollCallback<ScrollEvent>Scroll event with metrics
on_scroll_toCallback<usize>Target scroll offset
color_strategyBox<dyn TextAreaColorStrategy>Syntax highlighting strategy
languageStringLanguage hint for syntax highlighting; use language_from_path(path) to resolve from a file path
themeStringSyntax theme name
imagesVec<ImageContent>Attached images
on_images_changeCallback<Vec<ImageContent>>Images list updated
image_modeTextAreaImageModeInline or Attachment
image_placeholderStringPlaceholder text for inline images
image_placeholder_styleStyleInline image placeholder style
image_placeholder_hover_styleStyleStyle patched over hovered inline image placeholders
on_image_pasteCallback<ImageContent>Legacy: image pasted via Ctrl+V
widthLengthWidth
heightLengthHeight
focusableboolParticipate in focus traversal; mouse selection and copy shortcuts still work when false

Use ctx.text_area_scrollbars(key) to read the resolved scrollbar visibility for a keyed TextArea from the previous frame. Unkeyed TextArea widgets are not tracked, and the first frame returns ScrollbarVisibility::default().

scroll_to_line(line) is a declarative one-shot target for a zero-based logical line. With wrapping enabled, it scrolls to that line's first visual row and clamps out-of-range targets to the available content. Smooth behavior applies only to this explicit target; use ScrollBehavior::smooth_adaptive() when you want duration to scale with the resolved row distance. Controlled scroll_offset and cursor auto-scroll stay immediate, and user scrolling/editing cancels an active target animation.

clear_bindings(bindings) adds per-widget single-key shortcuts for clearing all text. The clear runs inside the TextArea editor instead of requiring the app to replace the controlled value, preserving the widget's undo history; undo restores the previous text, cursor, and selection. key_interceptor runs before clear, and a matching clear binding wins over keymap clipboard bindings for that key. Clear emits the normal on_change and on_edit callbacks with a replace edit.

rust
// Recommended: use TextArea::bound to preserve cursor & selection
TextArea::bound(&ctx.state.editor)
    .line_numbers(true)
    .wrap(false)
    .h_scrollbar(true)
    .on_change(ctx.link().callback(Msg::TextChanged))

TextArea editor primitives

Keyed TextAreas expose previous-frame geometry with ctx.text_area_metrics(key) -> Option<TextAreaMetrics>. Metrics use tui_lipan::Rect and byte offsets plus TextPosition projections; the first frame, missing keys, and unkeyed text areas return None.

TextArea::decoration / decorations accept byte-range TextAreaDecorations. Range, WholeLine, and Underline styles render through the normal overlay path before selection; Underline also enables the underline modifier automatically. The old TextAreaDecorationKind::VirtualText variant is a deprecated no-op; use dedicated virtual text entries instead.

TextArea::virtual_text / virtual_texts accept TextAreaVirtualText for non-editable inlay hints and diagnostics. VirtualTextPlacement::Inline inserts styled columns before the anchor byte and shifts later visual columns without changing the buffer. A cursor at the anchor renders after the inline virtual text, while mouse clicks inside the virtual columns clamp to the anchor byte. VirtualTextPlacement::Eol appends styled text after the logical line's final visual row and does not participate in wrapping. TextAreaMetrics.position stays buffer-based for LSP round-tripping; cursor rect values include inline virtual width.

Use TextAreaGutter::new().line_numbers(...).signs(...) with TextArea::gutter to compose line numbers with sign/custom columns. Existing line_numbers, line_number_mode, gutter_lines, and gutter_inset builders remain supported.

Vim motions

TextArea::vim_motions(true) enables an opt-in, widget-layer modal motion mode for that TextArea only. Existing apps keep plain TextArea behavior unless they enable it. An enabled TextArea starts in TextAreaVimMode::Normal; i, a, I, or A enter Insert, v toggles characterwise Visual selection mode from Normal/Visual, and V enters linewise Visual selection mode from Normal. Use on_vim_mode_change to update app-owned status bars, frame titles, or other mode-aware chrome.

When caret_shape is not overridden, Vim mode uses a steady block cursor for Normal, Visual, and VisualLine, and a steady vertical bar for Insert. Non-Vim TextAreas keep the regular caret behavior, including selection-driven cursor hiding for non-Vim selections.

ModeKeyBehavior
InsertEscSwitch to TextAreaVimMode::Normal
Insertother keysExisting TextArea editing behavior
NormalEscClear pending count/command or hide visible search feedback, stay Normal
Normal1..9, then 0..9Build a count prefix for motions
Normal0Move to current logical line start when no count is pending
Normalh / lMove left/right by character
Normalj / kMove down/up; wrapped TextAreas use visual-line navigation where available
Normalw / b / eMove to next word start, previous word start, or word end; punctuation runs and word characters are separate
NormalW / B / EMove by Vim WORDs: contiguous non-whitespace runs such as open-code or path/to/file
Normal$Move to current logical line end
Normalgg / GMove to first/last line; counts target one-based line numbers (Ngg, NG)
NormalvEnter TextAreaVimMode::Visual and anchor the selection at the cursor
NormalVEnter TextAreaVimMode::VisualLine and select whole logical lines
Normali / aEnter Insert at the cursor / after moving right once if possible
NormalI / AEnter Insert at first non-blank / end of the current logical line
NormaluUndo the previous edit group
Normalctrl+rRedo the next edit group
NormalyyYank the current logical line (y enters operator-pending mode)
Normald{motion} / ddDelete a motion range / whole logical lines, yanking before delete
Normalc{motion} / ccDelete a motion range / whole logical lines, yank it, and enter Insert
Normaldw / cwDelete/change by Vim word motion; cw changes the current word
Normalx / XDelete characters after / before the cursor, yanking before delete
Normalo / OOpen a new indented line below / above and enter Insert
Normalp / PPaste after / before the cursor or current logical line
Normal.Repeat the last Vim change command supported by TextArea
Normal/text Enter / ?text EnterSearch forward / backward; Esc cancels pending search input
Normaln / NRepeat the last search in the same / opposite direction
Normalm{a-z}Set a mark at the current byte cursor
Normal'a / `aJump to mark line first non-blank / exact mark cursor
Normal"{reg}Select a register for the next yank/delete/change/paste (+, _, 0..9, a..z)
Normal operatoriw / aw, iW / aWInner / around word or WORD text object
Normal operatorip / apInner / around paragraph text object
Normal operatorquotes/bracketsText objects for ', ", `, (), [], {}, and <>
Visualsupported motion keysMove the cursor and extend the cursor/anchor selection
VisualyYank the active selection and return to TextAreaVimMode::Normal
Visuald / xDelete the active selection, yank it, and return to TextAreaVimMode::Normal
VisualcDelete the active selection, yank it, and enter Insert
Visualp / PReplace the active selection with pasted text and return to TextAreaVimMode::Normal
VisualvExit Visual mode and return to TextAreaVimMode::Normal
VisualVExit Visual mode and return to TextAreaVimMode::Normal
VisualEscExit Visual mode, clear pending input, and return to TextAreaVimMode::Normal
VisualLinesupported motion keysMove by supported motions and extend the selection to whole logical lines
VisualLineyYank the whole-logical-line selection and return to TextAreaVimMode::Normal
VisualLined / xDelete the whole-logical-line selection, yank it, and return to TextAreaVimMode::Normal
VisualLinecDelete the whole-logical-line selection, yank it, and enter Insert
VisualLinep / PReplace the whole-logical-line selection with pasted text and return to TextAreaVimMode::Normal
VisualLinev / VExit linewise Visual mode and return to TextAreaVimMode::Normal
VisualLineEscExit linewise Visual mode, clear pending input, and return to TextAreaVimMode::Normal

Normal mode blocks unsupported printable and text-editing keys so accidental typing does not mutate the buffer. Characterwise Visual mode uses the same supported motions as Normal mode, but moves extend the active selection instead of clearing it. Linewise Visual mode selects whole logical lines, not soft-wrapped visual rows; each selected line includes its trailing newline except the final line when the buffer has no trailing newline. Vim mode uses Normal u and ctrl+r for undo/redo; Ctrl+Z and Ctrl+Y are not the Vim undo/redo path. Registers are TextArea-local except the unnamed and + registers, which also write/read the runtime clipboard. The black-hole register (_) discards yanks/deletes, 0 stores the latest yank, and 1..9 track recent deletes/changes.

Pending / and ? searches render a dedicated bottom search bar on the focused TextArea. The bar owns the full inner row, including line-number/custom gutter space, uses   for forward search and   for backward search, moves the terminal cursor into the query bar while typing, and right-aligns the current match count ([2/5]). Visible matches stay underlined after Enter, and the current match gets the configured current-match background highlight as n / N navigate through results. After Enter, the bottom search bar disappears and the [current/total] count is mirrored after the text on the visible row containing the current match. Normal Esc hides the visible search highlights/count without forgetting the stored query, so n / N can show and repeat it again.

Use TextArea::vim_config(...) for Vim-only visual affordances such as the search bar style, search-match/current-match styles, current-line highlighting, or the current line number/custom gutter style:

rust
TextArea::bound(&ctx.state.editor)
    .vim_motions(true)
    .vim_config(
        TextAreaVimConfig::new()
            .search_bar_prefix_style(Style::new().fg(Color::Cyan))
            .search_bar_count_style(Style::new().fg(Color::Yellow))
            .current_line_highlight(TextAreaVimCurrentLineHighlight::Full)
            .current_line_number_style(Style::new().fg(Color::Yellow).bold()),
    )

Clipboard shortcuts, configured clear bindings, key_interceptor, non-mutating navigation actions, and app/global actions keep their existing precedence. Vim bindings are not loaded from keymap.conf; use TextArea::vim_keymap(...) for widget-local aliases to canonical Vim command characters:

rust
let vim_keymap = TextAreaVimKeymap::new()
    .bind(KeyBindings::from_str("ctrl+n")?, 'j')
    .bind(KeyBindings::from_str("ctrl+p")?, 'k');

TextArea::bound(&ctx.state.editor)
    .vim_motions(true)
    .vim_keymap(vim_keymap)

Mutating clipboard operations and clear bindings leave Visual or VisualLine mode and clear the visual anchor after they run. Vim support is still TextArea-only: it does not affect Input or TextEditor directly and does not render a built-in mode label.

Mouse-created selections participate in Vim mode: double-click, triple-click, and drag ranges enter TextAreaVimMode::Visual automatically. Current-line row highlighting is suppressed while Visual/VisualLine selections are active. In VisualLine mode (V), the emitted cursor/anchor still cover whole logical lines, but the terminal caret is drawn on the active line at the original column clamped to that line's length.

Custom inline sentinels (extmarks)

Styled atomic tokens in the buffer use a separate PUA range from inline images: SENTINEL_BASE (U+F000). Entry i in sentinels maps to the single character U+F000 + i in value. One backspace/delete removes the whole codepoint; the framework prunes the parallel sentinels list and can emit SentinelEvent batches (see docs/enums.md and docs/events.md).

Type / APIRole
TextAreaSentinelLabel, normal/focus/hover styles, optional type-erased payload, optional SentinelId (or assign via insert_sentinel)
insert_sentinel(value, cursor, sentinels, sentinel)Insert at cursor; assigns SentinelId::next() when id is unset
TextAreaSnapshotcapture / apply / diff for in-memory stash–restore

Prefer on_sentinel_event for cleanup keyed by stable id; keep on_sentinels_change if you only need the pruned list. Use on_sentinel_click when a sentinel should open or expand app-owned content stored in its payload. Use TextAreaSentinel::hover_style for hover affordances such as ColorTransform::Lighten.

Example: examples/text_area_sentinels.rs

Syntax Highlighting (requires feature syntax-syntect)

rust
TextArea::new(code.clone())
    .with_syntax("rust", "base16-ocean.dark")

// With background colors
    .with_syntax_bg("rust", "one-dark")

// Auto-detect language from file path (extension/filename matching, no I/O)
TextArea::new(code.clone())
    .language_from_path("src/main.rs")   // resolves to "Rust"
    .with_syntax_strategy(SyntectStrategy::default(), "Rust", "base16-ocean.dark")

// Or use the free function to get the language string yourself
if let Some(lang) = tui_lipan::language_from_path(&file_path) {
    area = area.language(lang);
}

// Custom theme from file
    .with_syntax_custom_theme_from_file("rust", "MyTheme", "/path/to/theme.tmTheme")

Built-in themes: Catppuccin Frappe, Catppuccin Latte, Catppuccin Macchiato, Catppuccin Mocha, Dracula, InspiredGitHub, Monokai Extended, One Dark (Atom), Solarized (dark), Solarized (light), base16-eighties.dark, base16-mocha.dark, base16-ocean.dark, base16-ocean.light.

The bundled Syntect defaults do not include TypeScript/TSX grammars. When .language_from_path(...) sees .ts or .tsx and no exact grammar is present, it falls back to JavaScript highlighting instead of leaving the content plain.

SyntectStrategy::use_background(true) - apply syntax theme backgrounds (default: false).

When a TextArea, DocumentView, or DiffView uses SyntectStrategy, the app theme can now gently recolor token categories via Theme::syntax(...) while still using the selected syntect theme for tokenization and base styling.

Image Modes (requires feature image)

Inline mode (images embedded as Unicode PUA sentinels in text value):

rust
TextArea::new(self.input.clone())
    .image_mode(TextAreaImageMode::Inline)
    .images(self.images.clone())
    .on_images_change(ctx.link().callback(Msg::ImagesChanged))
    .image_placeholder("[Img]")
    .image_placeholder_style(Style::new().fg(Color::Magenta).bold())

Sentinel characters (U+E000…) represent images in the text value. Cursor movement and deletion work naturally. Use IMAGE_SENTINEL_BASE to construct sentinel chars manually if needed.

Inline image placeholders can be made interactive with on_sentinel_click; the event reports the image index and ImageContent for the clicked placeholder. Use image_placeholder_hover_style to add hover affordance to these image labels.

Attachment mode (images in separate list, text value unchanged):

rust
VStack::new()
    .gap(0)
    .child(
        if !self.images.is_empty() {
            DraggableTabBar::new()
                .tabs(self.images.iter().enumerate().map(|(i, _)| {
                    DraggableTab::new(format!("Image {}", i + 1)).closeable(true)
                }))
                .active(usize::MAX)
                .close_symbol("x")
                .draggable(false)
                .focusable(false)
                .on_close(ctx.link().callback(Msg::RemoveImage))
                .into()
        } else { Element::empty() }
    )
    .child(
        TextArea::new(self.input.clone())
            .image_mode(TextAreaImageMode::Attachment)
            .images(self.images.clone())
            .on_images_change(ctx.link().callback(Msg::ImagesChanged))
    )

Image pasting is opt-in: only active when on_images_change or on_image_paste is set. Without these, Ctrl+V pastes text only.


DiffView (requires feature diff-view)

Diff viewer with pluggable backends:

  • DiffViewBackend::TextArea (default) - supports editable mode
  • DiffViewBackend::DocumentView - read-only review + selection optimized

Backend selection rules:

  • Explicit .backend(...) always wins.
  • If backend is not set explicitly, calling .document_view(...) switches to DocumentView.
  • If backend is not set explicitly, calling .text_area(...) switches to TextArea.
  • Outer DiffView sizing inherits the active backend's width/height unless you override it with .width(...) / .height(...).
  • Pane borders are rendered by internal Frame wrappers, not by inner TextArea/DocumentView borders.

Line numbers in DiffView are source-mapped (git-style), not visual-row counters:

  • Split left pane shows original (before) line numbers.
  • Split right pane shows modified (after) line numbers.
  • Unified mode uses original numbers for removed lines, and modified numbers for added/context lines.
PropTypeDescription
beforeStringConstructor (first arg) - original text
afterStringConstructor (second arg) - modified text
modeDiffViewModeSplit (default) or Unified
backendDiffViewBackendTextArea (default) or DocumentView
editableboolEnable editing (TextArea backend only)
widthLengthOverride outer diff view width (otherwise inherit active backend width)
heightLengthOverride outer diff view height (otherwise inherit active backend height)
wrapboolApply wrapping to both backends
line_numbersboolToggle line numbers in both backends
min_line_number_widthu8Minimum gutter digits in both backends
gutter_insetu16Empty cells before the gutter / line numbers in both backends
borderboolToggle outer border around the whole DiffView
panels_borderboolToggle per-pane wrapper borders
scrollbarboolToggle vertical scrollbar in both backends
h_scrollbarboolToggle horizontal scrollbar in both backends
focusableboolToggle focusability in both backends
single_scrollbarboolIn split mode, show vertical scrollbar only on right pane
join_frameboolJoin split-pane wrapper frames (Frame::join_frame)
vertical_separatorboolInsert vertical divider between split panes
vertical_separator_charcharCharacter used by split divider
vertical_separator_styleStyleStyle for split divider
highlight_full_widthboolExtend changed-line background highlight to full row width
word_diffboolWord-level diff highlighting
trim_common_indentboolTrim the smallest shared leading indent from visible diff lines (default: true)
show_prefixesboolShow +/- prefix symbols
diff_styleDiffPaletteAdded/removed/context-separator/patch-header styles
neutral_bgColorConvenience setter for context/unchanged line background
base_color_strategyBox<dyn TextAreaColorStrategy>Syntax highlighting base strategy
languageStringLanguage hint; use language_from_path(path) to resolve from a file path
themeStringSyntax theme name
text_areaTextAreaPre-configured TextArea for scroll/border/wrap settings
document_viewDocumentViewPre-configured DocumentView for scroll/border/wrap settings
scroll_offsetusizeControlled scroll offset (applies to rendered pane(s))
scroll_to_hunkusizeScroll rendered pane(s) to a zero-based parsed patch hunk index; resolved after trim/context collapse and before backend wrap layout
context_linesusizeCollapse unchanged regions farther than n lines from any change into a separator line (default: show all)
show_context_separatorboolShow/hide the context separator placeholder when collapsing context (default: true)
context_separator_textStringTemplate for context separator text; supports {count}, {line_word}, {direction}, {arrow}
context_separator_hover_styleStyleStyle patched over a context separator while the pointer hovers it
context_separator_min_linesusizeMinimum hidden lines before a separator is shown; shorter runs render as normal context (default: 2)
context_expand_linesusizePer-click reveal size used by DiffContextSeparatorEvent::next_expansion (default: 20)
expanded_contextsIntoIterator<Item = DiffContextRange>Controlled set of collapsed context ranges that should render fully expanded
expanded_context_expansionsIntoIterator<Item = DiffContextExpansion>Controlled partial or full expansions
expanded_contextDiffContextRangeConvenience setter to fully expand one collapsed context range
expanded_context_lines(DiffContextRange, usize)Expand one collapsed range by a specific number of lines
on_context_separator_clickCallback<DiffContextSeparatorEvent>Fires when a visible context separator line is clicked
shared_selection_idArc<str>Cross-widget selection group id (unified: as-is; split: auto-suffixed :left/:right, while plain DocumentViews using the unsuffixed base id can still drag into split panes)
on_scrollCallback<DiffScrollEvent>Pane-aware scroll callback (pane + ScrollEvent)
rust
let diff = DiffView::new(before, after)
    .mode(DiffViewMode::Split)
    .document_view(DocumentView::new("")) // backend inferred
    .height(Length::Auto) // useful for inline/message-style diff blocks
    .border(true)
    .panels_border(true)
    .wrap(true)
    .line_numbers(true)
    .min_line_number_width(4)
    .single_scrollbar(true)
    .join_frame(true)
    .vertical_separator(true)
    .vertical_separator_style(Style::new().dim())
    .highlight_full_width(true)
    .neutral_bg(Color::rgb(24, 24, 24))
    .word_diff(true)
    .show_prefixes(true);

By default, `DiffView` derives its added/removed/marker styling from `Theme::diff`.
Use `.diff_style(...)` only when you want per-widget overrides.

`DiffView` also trims shared leading indentation by default so deeply indented code is easier to read inline. Use `.trim_common_indent(false)` when you need the original left margin preserved exactly.

With the `DocumentView` backend in split mode, dragging selection across the
divider selects both panes row-by-row. Copy shortcuts copy that cross-pane
selection as tab-separated logical diff rows (`left\tright`), collapsing any
soft-wrapped visual rows back into their original diff line.

// Efficient: build DiffData once, reuse across renders
let data = DiffData::with_config(before, after, config);
let diff = DiffView::new(before, after).with_diff(data);
// or: DiffView::new(before, after).with_shared_diff(Arc<DiffData>)

// Patch navigation: anchors are logical rendered rows, not final wrapped rows.
// Use scroll_to_hunk(index) so the backend resolves wrapping during layout.
let patch_data = DiffData::from_patch(patch);
let hunk_count = patch_data.hunk_anchors(DiffViewMode::Unified).len();
let diff = DiffView::from_patch(patch)
    .mode(DiffViewMode::Unified)
    .scroll_to_hunk(selected_hunk.min(hunk_count.saturating_sub(1)));

// DiffData::hunk_anchors_for_pane(DiffPane::Right) is also available when an
// app needs pane-specific logical anchors. Before/after content diffs have no
// patch hunk anchors unless built from a unified patch.

// DiffDataConfig fields:
// - word_diff: bool        - precompute word-level diff tokens
// - context_lines: Option<usize> - collapse unchanged regions (same as the prop)
// - show_context_separator: bool   - insert separator placeholder (default: true)
// - context_separator_text: Arc<str> - template for separator text

// Split scroll sync pattern (controlled):
let mut offset: Option<usize> = None;
let mut diff = DiffView::new(before, after)
    .mode(DiffViewMode::Split)
    .on_scroll(ctx.link().callback(|ev: DiffScrollEvent| Msg::DiffScrolled(ev)));
if let Some(v) = offset {
    diff = diff.scroll_offset(v);
}

// Custom diff colors (lines + markers + line numbers):
let style = DiffPalette {
    added: Style::new().bg(Color::rgb(0x14, 0x2F, 0x20)),
    removed: Style::new().bg(Color::rgb(0x3B, 0x1E, 0x24)),
    context_line_number: Style::new().fg(Color::DarkGray), // fg/bg for unchanged line numbers in gutter
    added_marker: Style::new().fg(Color::Green),
    removed_marker: Style::new().fg(Color::Red),
    added_line_number: Style::new().fg(Color::DarkGray),   // fg/bg for added line numbers in gutter
    removed_line_number: Style::new().fg(Color::DarkGray), // fg/bg for removed line numbers in gutter
    context_separator_style: Style::new().fg(Color::DarkGray).dim(), // style for context-collapse separator lines
    patch_header: Style::new().fg(Color::Cyan).bold(), // style for in-band `diff --git ...` metadata lines
    ..DiffPalette::default()
};

// Context lines - collapse unchanged regions far from changes:
DiffView::new(before, after)
    .context_lines(3) // show 3 lines of context around each change
    .context_separator_text("{arrow} {count} {line_word} omitted {direction}")
    .show_context_separator(false) // omit separator placeholders entirely
    .mode(DiffViewMode::Unified);
// Separator lines are excluded from copy operations.
// Both Split and Unified modes support context_lines.
// Default separator text is arrow-based and direction-aware, e.g. "↑ 9 hidden lines above ↑".
// The separator style is themed by default (dimmed muted text); override via DiffPalette::context_separator_style.
// Raw patch metadata lines like `diff --git a/... b/...` stay in the scrollable diff and use DiffPalette::patch_header.

// Click-to-expand pattern (controlled by your component state):
DiffView::new(before, after)
    .context_lines(3)
    .context_expand_lines(20)
    .expanded_context_expansions(expansions.clone())
    .context_separator_text("+ show {count} {line_word} {direction}")
    .context_separator_hover_style(Style::new().underline())
    .on_context_separator_click(ctx.link().callback(Msg::ExpandDiffContext));
// In update(), find the current expansion by ev.range, then store ev.next_expansion(current).
// Scroll position is preserved automatically when context lines are revealed.

// Auto-detect language from file path (requires feature `syntax-syntect`):
DiffView::new(before, after)
    .language_from_path("src/main.rs")  // resolves to "Rust", no-op if unknown
    .theme("One Dark (Atom)");          // set theme separately; with_syntax() would override the detected language

For .ts and .tsx paths, DiffView::language_from_path(...) uses the same JavaScript fallback as TextArea when the active Syntect syntax set has no TypeScript/TSX grammar.


Checkbox

Toggle widget for boolean values.

PropTypeDescription
checkedboolConstructor - checked state
stateCheckboxStateFull state (overrides checked)
indeterminateboolShow indeterminate state
labelStringLabel text
variantCheckboxVariantVisual variant
gapu16Space between box and label
styleStyleIdle style
hover_styleStyleHover style
extend_hover_style / inherit_hover_styleStyle / ()Extend or inherit the hover theme role instead of replacing it
focus_styleStyleFocus style
extend_focus_style / inherit_focus_styleStyle / ()Extend or inherit the focus theme role instead of replacing it
checked_styleStyleStyle when checked
unchecked_styleStyleStyle when unchecked
indeterminate_styleStyleStyle when indeterminate
label_styleStyleLabel style
paddingimpl Into<Padding>Padding
disabledboolDisable interaction
disabled_styleStyleStyle when disabled
on_toggleCallback<bool>Toggle callback
on_clickCallback<()>Click callback

Radio

Radio button group.

PropTypeDescription
optionsVec<Arc<str>>Constructor - option labels
selectedOption<usize>Selected index
layoutRadioLayoutVertical or Horizontal
variantRadioVariantVisual variant
gapu16Space between options
styleStyleBase style
checked_styleStyleSelected item style
unchecked_styleStyleUnselected item style
hover_styleStyleHover style
focus_styleStyleFocus style
label_styleStyleLabel style
disabledboolDisable interaction
disabled_styleStyleStyle when disabled
on_changeCallback<usize>Selection changed callback
rust
Radio::new(vec!["Option A".into(), "Option B".into(), "Option C".into()])
    .selected(Some(self.choice))
    .layout(RadioLayout::Horizontal)
    .on_change(ctx.link().callback(Msg::ChoiceChanged))

Select

Dropdown select widget.

PropTypeDescription
optionsVec<String>Available options
selectedOption<usize>Selected index
placeholderStringPlaceholder when nothing selected
expandedboolControlled expanded state
widthLengthWidth
disabledboolDisable interaction
on_selectCallback<usize>Item selected
on_changeCallback<usize>Selection changed
on_toggleCallback<bool>Dropdown opened/closed
button_variantButtonVariantTrigger button variant
button_styleStyleButton idle style
button_hover_styleStyleButton hover style
extend_button_hover_style / inherit_button_hover_styleStyle / ()Extend or inherit the button hover theme role
button_focus_styleStyleButton focus style
extend_button_focus_style / inherit_button_focus_styleStyle / ()Extend or inherit the button focus theme role
button_disabled_styleStyleButton disabled style
button_border_styleBorderStyleTrigger border style (outlined variant)
button_hover_border_styleBorderStyleTrigger border style when hovered
button_focus_border_styleBorderStyleTrigger border style when focused
button_open_suffixStringTrigger suffix while expanded
button_closed_suffixStringTrigger suffix while collapsed
button_suffix_styleStyleTrigger suffix style
list_titleStringDropdown title (bordered list)
list_title_styleStyleDropdown title style
list_borderboolDropdown list border
list_border_styleBorderStyleDropdown border style
list_paddingimpl Into<Padding>Dropdown padding
list_styleStyleDropdown list style
list_selection_styleStyleSelected item style
extend_list_selection_style / inherit_list_selection_styleStyle / ()Extend or inherit the selection theme role instead of replacing it
list_unfocused_selection_styleStyleSelected item style while dropdown list is not focused; defaults to list_selection_style
extend_list_unfocused_selection_style / inherit_list_unfocused_selection_styleStyle / ()Extend or inherit the unfocused selection theme role instead of replacing it
list_selection_full_widthboolExtend selection style across full row
list_selection_symbolOption<String>Selection symbol for selected row
list_selection_symbol_styleStyleSelection symbol style
list_unfocused_selection_symbol_styleStyleSelection symbol style while dropdown list is not focused; defaults to list_selection_symbol_style
list_hover_styleStyleHover style in list
extend_list_hover_style / inherit_list_hover_styleStyle / ()Extend or inherit the list hover theme role
list_widthLengthDropdown width override
list_heightLengthDropdown height override
match_button_widthboolForce dropdown width to trigger width
list_scrollbarboolScrollbar in list
list_scrollbar_configScrollbarConfigFull scrollbar configuration (variant, gap, thumb, thumb styles)
list_empty_textStringDropdown empty text
list_empty_text_styleStyleDropdown empty text style
list_disabled_styleStyleDropdown disabled style

Select forwards shared dropdown-list chrome through ListConfig, including symbol_column, gutter_gap, and gutter_for_non_selectable for row-local leading adornment alignment.


ComboBox

Controlled input + dropdown list for searchable selection.

PropTypeDescription
itemsVec<Arc<str>>Source options
queryArc<str>Controlled input value
placeholderStringInput placeholder
openboolControlled dropdown state
active_indexOption<usize>Active source index
selectedOption<usize>Selected source index fallback
allow_custom_valueboolAllow Enter to commit free-form query
widthLengthInput width
list_widthLengthDropdown width override
list_heightLengthDropdown height
list_selection_styleStyleActive dropdown item style
extend_list_selection_style / inherit_list_selection_styleStyle / ()Extend or inherit the selection theme role instead of replacing it
list_unfocused_selection_styleStyleActive dropdown item style while dropdown list is not focused; defaults to list_selection_style
extend_list_unfocused_selection_style / inherit_list_unfocused_selection_styleStyle / ()Extend or inherit the unfocused selection theme role instead of replacing it
list_unfocused_selection_symbol_styleStyleActive dropdown item symbol style while dropdown list is not focused; defaults to list_selection_symbol_style
match_input_widthboolForce dropdown width to match input width
disabledboolDisable interaction
input_hover_styleStyleInput hover style
extend_input_hover_style / inherit_input_hover_styleStyle / ()Extend or inherit the input hover theme role
input_focus_styleStyleInput focus style
extend_input_focus_style / inherit_input_focus_styleStyle / ()Extend or inherit the input focus theme role
input_disabled_styleStyleInput disabled style
input_hover_border_styleBorderStyleInput border style while hovered
input_open_suffixStringSuffix when dropdown is open
input_closed_suffixStringSuffix when dropdown is closed
input_suffix_styleStyleInput suffix style
input_focus_suffix_styleStyleInput suffix style when focused
on_query_changeCallback<Arc<str>>Input query changed
on_open_changeCallback<bool>Request open/close change
on_active_index_changeCallback<Option<usize>>Active source index changed
on_commitCallback<ComboBoxCommitEvent>Enter/activate commit event

ComboBoxCommitEvent contains:

  • index: Option<usize>
  • value: Arc<str>
  • from_custom_value: bool

Interaction notes:

  • With match_input_width(true), dropdown width follows the rendered trigger/input width.
  • ComboBox forwards dropdown-list chrome through ListConfig, including symbol_column, gutter_gap, and gutter_for_non_selectable.

MultiSelect

Controlled list for selecting multiple items with Space toggle and Enter commit.

PropTypeDescription
itemsimpl Iterator<Item = impl Into<MultiSelectItem>>Source rows
active_indexusizeActive source index
selected_indicesVec<usize>Controlled selected source indices
max_selectedusizeOptional maximum selected rows
selected_prefixStringPrefix for selected rows (default: [x])
unselected_prefixStringPrefix for unselected rows (default: [ ])
description_styleStyleStyle used for item descriptions
description_placementMultiSelectDescriptionPlacementDescription placement: Inline, Right, Above, Below
description_overflowMultiSelectDescriptionOverflowDescription overflow policy: Truncate or Wrap (Wrap applies to Above/Below)
description_selectionboolWhether selection highlight applies to descriptions
widthLengthWidth
heightLengthHeight
titleStringList title (requires border)
title_styleStyleList title style
selection_styleStyleActive row style
extend_selection_style / inherit_selection_styleStyle / ()Extend or inherit the selection theme role instead of replacing it
unfocused_selection_styleStyleActive row 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
unfocused_selection_symbol_styleStyleActive row symbol style while list is not focused; defaults to selection_symbol_style
selection_full_widthboolExpand selection style across row width
disabledboolDisable interaction
disabled_styleStyleStyle when disabled
empty_textStringText when list is empty
empty_text_styleStyleEmpty-text style
on_active_index_changeCallback<usize>Active row changed
on_toggleCallback<MultiSelectToggleEvent>Current row toggled
on_changeCallback<MultiSelectChangeEvent>Selected set changed
on_commitCallback<MultiSelectCommitEvent>Enter commit with selected set

Event payloads:

  • MultiSelectToggleEvent { index, selected }
  • MultiSelectChangeEvent { selected_indices }
  • MultiSelectCommitEvent { selected_indices }

Interaction notes:

  • Space toggles the active_index row.
  • Mouse click on a row toggles that row and updates the active row.
  • Enter emits on_commit (mouse click does not commit).

MultiSelectItem supports optional descriptions:

rust
MultiSelectItem::new("Cargo.toml")
    .description("Workspace manifest")

description_selection(false) only affects Above/Below description lines. For Inline and Right, selection/hover styling still applies to the full row.

description_overflow(MultiSelectDescriptionOverflow::Wrap) affects only Above/Below placement. Inline and Right keep single-row truncation behavior.

MultiSelect forwards its inner List chrome through ListConfig, including symbol_column, gutter_gap, and gutter_for_non_selectable for consistent marker-column alignment.


HexArea

Hex/ASCII binary data viewer with keyboard cursor navigation.

PropTypeDescription
bytesArc<[u8]>Constructor - byte buffer to render
cursorusizeControlled cursor byte index
anchorOption<usize>Optional selection anchor byte index
read_onlyboolRead-only mode flag
bytes_per_rowu16Number of bytes rendered per row
show_asciiboolShow ASCII preview column
show_offsetsboolShow hexadecimal offset gutter
uppercase_hexboolUppercase (AA) or lowercase (aa) hex output
scroll_offsetOption<usize>Controlled row scroll offset
styleStyleBase style
hover_styleStyleHover style
extend_hover_style / inherit_hover_styleStyle / ()Extend or inherit the hover theme role instead of replacing it
focus_styleStyleFocus style
extend_focus_style / inherit_focus_styleStyle / ()Extend or inherit the focus theme role instead of replacing it
selection_styleStyleByte-range selection style
extend_selection_style / inherit_selection_styleStyle / ()Extend or inherit the text-selection theme role instead of replacing it
cursor_styleStyleCursor byte style
pending_edit_styleStyleBackground/style for half-entered nibble edits
borderboolDraw border
border_styleBorderStyleBorder style
paddingimpl Into<Padding>Inner padding
widthLengthWidth
heightLengthHeight
focusableboolParticipate in focus traversal
disabledboolDisable interaction
on_cursor_changeCallback<HexAreaCursorEvent>Emits on cursor movement (keyboard/mouse)
on_changeCallback<HexAreaChangeEvent>Emits updated bytes after edits
on_editCallback<HexAreaEditEvent>Emits per-edit metadata (replace/insert/delete)
on_scrollCallback<ScrollEvent>Emits desired row offset during navigation/wheel scrolling
on_keyKeyHandlerCustom key handler fallback

HexAreaCursorEvent contains:

  • cursor: usize
  • anchor: Option<usize>

Interaction notes:

  • Hex and ASCII columns are both clickable and map to the same byte index.
  • Click-and-drag creates/extends a byte-range selection.
  • First typed hex digit clears the cell to <digit> and enters pending-nibble mode.
  • Pending-nibble mode highlights the edited cell with pending_edit_style.
  • Esc cancels pending-nibble mode and restores the original byte.

HexAreaEditEvent contains:

  • index: usize
  • before: Option<u8>
  • after: Option<u8>
  • kind: HexAreaEditKind

Edit keys (when read_only(false) and on_change is set):

  • Hex digits (0-9, a-f) replace bytes in two-keystroke nibble mode
  • Insert inserts 0x00 at cursor
  • Delete removes byte at cursor
  • Backspace removes byte before cursor
  • Ctrl+Z undo, Ctrl+Shift+Z/Ctrl+Y redo

Slider

Numeric selection slider.

PropTypeDescription
valuef64Constructor - current value
minf64Minimum value
maxf64Maximum value
stepf64Step increment
labelStringLabel text
show_valueboolShow current value
thumb_symbolcharThumb character
track_symbolcharEmpty track character
filled_track_symbolcharFilled track character
hover_thumb_symbolcharThumb on hover
styleStyleBase style
filled_track_styleStyleFilled portion style
filled_track_gradientColorGradientFilled portion gradient
thumb_styleStyleThumb style
thumb_gradientColorGradientThumb gradient
focus_styleStyleFocus style
extend_focus_style / inherit_focus_styleStyle / ()Extend or inherit the focus theme role instead of replacing it
focus_thumb_styleStyleThumb when focused
extend_focus_thumb_style / inherit_focus_thumb_styleStyle / ()Extend or inherit the focus theme role for the thumb
hover_thumb_styleStyleThumb on hover
extend_hover_thumb_style / inherit_hover_thumb_styleStyle / ()Extend or inherit the hover theme role for the thumb
label_styleStyleLabel style
paddingimpl Into<Padding>Padding
widthLengthWidth
heightLengthHeight
focusableboolAccept focus
on_changeCallback<f64>Value changed
on_clickCallback<f64>Click / Enter

DatePicker

Calendar-based date selection.

PropTypeDescription
yeari32Current year
monthu32Current month (1–12)
dayOption<u32>Selected day
titleStringCalendar title
show_outside_daysboolShow days from adjacent months
borderboolDraw border
border_styleBorderStyleBorder appearance
paddingimpl Into<Padding>Padding
styleStyleBase style
header_styleStyleMonth/year header style
weekday_styleStyleWeekday name row style
day_styleStyleRegular day style
day_hover_styleStyleDay hover style
extend_day_hover_style / inherit_day_hover_styleStyle / ()Extend or inherit the hover theme role for day cells
selected_styleStyleSelected day style
outside_month_styleStyleAdjacent-month day style
nav_styleStyleNavigation button style
nav_hover_styleStyleNavigation button hover
extend_nav_hover_style / inherit_nav_hover_styleStyle / ()Extend or inherit the hover theme role for navigation buttons
nav_disabled_styleStyleDisabled navigation style
widthLengthWidth
heightLengthHeight
on_selectCallback<(i32, u32, u32)>Day selected (year, month, day)
on_prev_monthCallback<()>Previous month navigation
on_next_monthCallback<()>Next month navigation

MIT OR Apache-2.0