pub struct Model<I: Item> { /* private fields */ }Expand description
The main list component model.
Model<I> is a generic list component that can display any items implementing
the Item trait. It provides filtering, navigation, pagination, and customizable
rendering through the delegate pattern.
§Type Parameters
I- The item type that must implementItem + Send + Sync + 'static
§Examples
use bubbletea_widgets::list::{Model, DefaultDelegate, DefaultItem};
let items = vec![
DefaultItem::new("Apple", "Red fruit"),
DefaultItem::new("Banana", "Yellow fruit"),
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);A flexible, interactive list component with filtering, pagination, and customizable rendering.
The Model<I> is the main list component that can display any items implementing the Item trait.
It provides fuzzy filtering, keyboard navigation, viewport scrolling, help integration, and
customizable styling through delegates.
§Features
- Fuzzy filtering: Real-time search with character-level highlighting
- Smooth scrolling: Viewport-based navigation that maintains context
- Customizable rendering: Delegate pattern for complete visual control
- Keyboard navigation: Vim-style keys plus standard arrow navigation
- Contextual help: Automatic help text generation from key bindings
- Responsive design: Adapts to different terminal sizes
- State management: Clean separation of filtering, selection, and view states
§Architecture
The list uses a viewport-based scrolling system that maintains smooth navigation context instead of discrete page jumps. Items are rendered using delegates that control appearance and behavior, while filtering uses fuzzy matching with character-level highlighting for search results.
§Navigation
- Up/Down: Move cursor through items with smooth viewport scrolling
- Page Up/Down: Jump by pages while maintaining cursor visibility
- Home/End: Jump to first/last item
- / : Start filtering
- Enter: Accept filter (while filtering)
- Escape: Cancel filter (while filtering)
- Ctrl+C: Clear active filter
§Filtering
The list supports fuzzy filtering with real-time preview:
- Type “/” to start filtering
- Type characters to filter items in real-time
- Matched characters are highlighted in the results
- Press Enter to accept the filter or Escape to cancel
§Styling
Visual appearance is controlled through the ListStyles struct and item delegates.
The list adapts to light/dark terminal themes automatically and supports
customizable colors, borders, and typography.
§Examples
use bubbletea_widgets::list::{Model, DefaultDelegate, DefaultItem};
let items = vec![
DefaultItem::new("Task 1", "Complete documentation"),
DefaultItem::new("Task 2", "Review pull requests"),
];
let delegate = DefaultDelegate::new();
let list = Model::new(items, delegate, 80, 24);§With Custom Items
use bubbletea_widgets::list::{Item, Model, DefaultDelegate};
use std::fmt::Display;
#[derive(Clone)]
struct CustomItem {
title: String,
priority: u8,
}
impl Display for CustomItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}] {}", self.priority, self.title)
}
}
impl Item for CustomItem {
fn filter_value(&self) -> String {
format!("{} priority:{}", self.title, self.priority)
}
}
let items = vec![
CustomItem { title: "Fix bug".to_string(), priority: 1 },
CustomItem { title: "Add feature".to_string(), priority: 2 },
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);Implementations§
Source§impl<I: Item + Send + Sync + 'static> Model<I>
impl<I: Item + Send + Sync + 'static> Model<I>
Sourcepub fn is_filtering(&self) -> bool
pub fn is_filtering(&self) -> bool
Returns true if filtering is currently active in any form.
This method provides a simple way to check if the list is in any filtering state (either actively typing a filter or has an applied filter). It’s useful for applications that need to conditionally show UI elements or change behavior based on filtering status.
§Returns
trueif inFilteringorFilterAppliedstatefalseif inUnfilteredstate
§Examples
use bubbletea_widgets::list::{Model, DefaultDelegate, DefaultItem, FilterState};
let items = vec![DefaultItem::new("Apple", "Red fruit")];
let mut list = Model::new(items, DefaultDelegate::new(), 80, 24);
assert!(!list.is_filtering()); // Initially not filtering
// Simulate applying a filter (would normally be done through user input)
list.set_filter_text("app");
// In a real application, filter would be applied through the update() method
assert!(!list.is_filtering()); // Still not filtering until state changesSourcepub fn clear_filter(&mut self) -> Option<Cmd>
pub fn clear_filter(&mut self) -> Option<Cmd>
Forces complete filter clearing in a single operation.
This method provides a programmatic way to completely clear any active filter, equivalent to the user pressing the clear filter key binding. It’s useful for applications that need to reset the list state or implement custom clear functionality.
§Returns
Returns None as no follow-up commands are needed for the clear operation.
§Effects
- Clears the filter input text
- Sets state to
Unfiltered - Clears filtered items list
- Resets cursor to position 0
- Updates pagination
§Examples
use bubbletea_widgets::list::{Model, DefaultDelegate, DefaultItem};
let items = vec![DefaultItem::new("Apple", "Red fruit")];
let mut list = Model::new(items, DefaultDelegate::new(), 80, 24);
// Apply a filter (this would normally be done through user interaction)
list.set_filter_text("app");
// Clear it programmatically
let cmd = list.clear_filter();
assert!(cmd.is_none()); // Returns None
assert!(!list.is_filtering()); // No longer filteringSourcepub fn filter_state_info(&self) -> FilterStateInfo
pub fn filter_state_info(&self) -> FilterStateInfo
Returns detailed information about the current filter state.
This method provides comprehensive information about filtering without requiring direct access to internal fields. It’s particularly useful for applications that need to display filter status information or make decisions based on detailed filter state.
§Returns
A FilterStateInfo struct containing:
- Current filter state enum
- Filter query text
- Number of matching items
- Whether filtering is active
- Whether currently in clearing state
§Examples
use bubbletea_widgets::list::{Model, DefaultDelegate, DefaultItem, FilterState};
let items = vec![
DefaultItem::new("Apple", "Red fruit"),
DefaultItem::new("Banana", "Yellow fruit"),
];
let mut list = Model::new(items, DefaultDelegate::new(), 80, 24);
// Check initial state
let info = list.filter_state_info();
assert_eq!(info.state, FilterState::Unfiltered);
assert_eq!(info.query, "");
assert_eq!(info.match_count, 2); // All items visible
assert!(!info.is_filtering);
assert!(!info.is_clearing);
// Set filter text (would be applied through user interaction)
list.set_filter_text("app");
let info = list.filter_state_info();
assert_eq!(info.query, "app"); // Query is setSource§impl<I: Item + Send + Sync + 'static> Model<I>
impl<I: Item + Send + Sync + 'static> Model<I>
Sourcepub fn new<D>(items: Vec<I>, delegate: D, width: usize, height: usize) -> Self
pub fn new<D>(items: Vec<I>, delegate: D, width: usize, height: usize) -> Self
Creates a new list with the provided items, delegate, and dimensions.
This is the primary constructor for creating a list component. The delegate controls how items are rendered and behave, while the dimensions determine the initial size for layout calculations.
§Arguments
items- Vector of items to display in the listdelegate- Item delegate that controls rendering and behaviorwidth- Initial width in terminal columns (can be updated later)height- Initial height in terminal rows (affects pagination)
§Returns
A new Model<I> configured with default settings:
- Title set to “List”
- 10 items per page
- Cursor at position 0
- All items initially visible (no filtering)
- Status bar enabled with default item names
§Examples
use bubbletea_widgets::list::{Model, DefaultDelegate, DefaultItem};
let items = vec![
DefaultItem::new("First", "Description 1"),
DefaultItem::new("Second", "Description 2"),
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);
assert_eq!(list.len(), 2);Sourcepub fn set_items(&mut self, items: Vec<I>)
pub fn set_items(&mut self, items: Vec<I>)
Sets the items displayed in the list.
This method replaces all current items with the provided vector. The cursor is reset to position 0, and pagination is recalculated based on the new item count.
§Arguments
items- Vector of new items to display
§Examples
let mut list = Model::new(vec![], DefaultDelegate::new(), 80, 24);
let items = vec![
DefaultItem::new("Apple", "Red fruit"),
DefaultItem::new("Banana", "Yellow fruit"),
];
list.set_items(items);
assert_eq!(list.len(), 2);Sourcepub fn visible_items(&self) -> Vec<I>
pub fn visible_items(&self) -> Vec<I>
Returns a vector of currently visible items.
The returned items reflect the current filtering state:
- When unfiltered: returns all items
- When filtered: returns only items matching the current filter
§Returns
A vector containing clones of all currently visible items.
§Examples
let items = vec![
DefaultItem::new("First", "Description 1"),
DefaultItem::new("Second", "Description 2"),
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);
let visible = list.visible_items();
assert_eq!(visible.len(), 2);Sourcepub fn set_filter_text(&mut self, s: &str)
pub fn set_filter_text(&mut self, s: &str)
Sets the filter text without applying the filter.
This method updates the filter input text but does not trigger the filtering process. It’s primarily used for programmatic filter setup or testing.
§Arguments
s- The filter text to set
§Examples
let mut list: Model<DefaultItem> = Model::new(vec![], DefaultDelegate::new(), 80, 24);
list.set_filter_text("search term");
// Filter text is set but not applied until filtering is activatedSourcepub fn set_filter_state(&mut self, st: FilterState)
pub fn set_filter_state(&mut self, st: FilterState)
Sets the current filtering state.
This method directly controls the list’s filtering state without triggering filter application. It’s useful for programmatic state management or testing specific filter conditions.
§Arguments
st- The new filter state to set
§Examples
let mut list: Model<DefaultItem> = Model::new(vec![], DefaultDelegate::new(), 80, 24);
list.set_filter_state(FilterState::Filtering);
// List is now in filtering modeSourcepub fn set_status_bar_item_name(&mut self, singular: &str, plural: &str)
pub fn set_status_bar_item_name(&mut self, singular: &str, plural: &str)
Sets custom singular and plural names for status bar items.
The status bar displays item counts using these names. If not set, defaults to “item” and “items”.
§Arguments
singular- Name for single item (e.g., “task”)plural- Name for multiple items (e.g., “tasks”)
§Examples
let mut list: Model<DefaultItem> = Model::new(vec![], DefaultDelegate::new(), 80, 24);
list.set_status_bar_item_name("task", "tasks");
// Status bar will now show "1 task" or "5 tasks"Sourcepub fn status_view(&self) -> String
pub fn status_view(&self) -> String
Renders the status bar as a formatted string.
The status bar shows the current selection position and total item count, using the custom item names if set. The format is “X/Y items” where X is the current cursor position + 1, and Y is the total item count.
§Returns
A formatted status string, or empty string if status bar is disabled.
§Examples
let items = vec![
DefaultItem::new("First", ""),
DefaultItem::new("Second", ""),
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);
let status = list.status_view();
assert!(status.contains("1/2"));Sourcepub fn matches_for_original_item(
&self,
original_index: usize,
) -> Option<&Vec<usize>>
pub fn matches_for_original_item( &self, original_index: usize, ) -> Option<&Vec<usize>>
Returns fuzzy match character indices for a given original item index.
This method finds the character positions that matched the current filter for a specific item identified by its original index in the full items list. These indices can be used for character-level highlighting in custom delegates.
§Arguments
original_index- The original index of the item in the full items list
§Returns
A reference to the vector of character indices that matched the filter,
or None if no matches exist for this item or if filtering is not active.
§Examples
let items = vec![DefaultItem::new("Apple", "Red fruit")];
let mut list = Model::new(items, DefaultDelegate::new(), 80, 24);
// Apply a filter first
list.set_filter_text("app");
// In a real application, this would be done through user interaction
if let Some(matches) = list.matches_for_original_item(0) {
// matches contains the character indices that matched "app" in "Apple"
println!("Matched characters at indices: {:?}", matches);
}Sourcepub fn with_title(self, title: &str) -> Self
pub fn with_title(self, title: &str) -> Self
Sets the list title.
The title is displayed at the top of the list when not filtering. During filtering, the title is replaced with the filter input interface.
§Arguments
title- The new title for the list
§Returns
Self, for method chaining.
§Examples
let list: Model<DefaultItem> = Model::new(vec![], DefaultDelegate::new(), 80, 24)
.with_title("My Tasks");Or using the mutable method:
let mut list: Model<DefaultItem> = Model::new(vec![], DefaultDelegate::new(), 80, 24);
list = list.with_title("My Tasks");Sourcepub fn selected_item(&self) -> Option<&I>
pub fn selected_item(&self) -> Option<&I>
Returns a reference to the currently selected item.
The selected item is the one at the current cursor position. If the list
is empty or the cursor is out of bounds, returns None.
§Returns
A reference to the selected item, or None if no valid selection exists.
§Examples
let items = vec![
DefaultItem::new("First", "Description 1"),
DefaultItem::new("Second", "Description 2"),
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);
if let Some(selected) = list.selected_item() {
println!("Selected: {}", selected);
}Sourcepub fn cursor(&self) -> usize
pub fn cursor(&self) -> usize
Returns the current cursor position.
The cursor represents the currently selected item index within the visible (possibly filtered) list. This is always relative to the currently visible items, not the original full list.
§Returns
The zero-based index of the currently selected item.
§Examples
let items = vec![
DefaultItem::new("First", "Description"),
DefaultItem::new("Second", "Description"),
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);
assert_eq!(list.cursor(), 0); // Initially at first itemSourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of currently visible items.
This count reflects the items actually visible to the user:
- When unfiltered: returns the total number of items
- When filtering is active: returns only the count of matching items
§Returns
The number of items currently visible in the list.
§Examples
let items = vec![
DefaultItem::new("Apple", "Red"),
DefaultItem::new("Banana", "Yellow"),
];
let list = Model::new(items, DefaultDelegate::new(), 80, 24);
assert_eq!(list.len(), 2);Trait Implementations§
Source§impl<I: Item> KeyMap for Model<I>
impl<I: Item> KeyMap for Model<I>
Source§fn short_help(&self) -> Vec<&Binding>
fn short_help(&self) -> Vec<&Binding>
Returns key bindings for compact help display.
Provides a minimal set of the most important key bindings based on the current list state. The bindings change depending on whether the user is actively filtering or navigating.
§Context-Sensitive Help
- While filtering: Shows Enter (accept) and Escape (cancel) bindings
- Normal navigation: Shows up/down navigation and filter activation
Source§fn full_help(&self) -> Vec<Vec<&Binding>>
fn full_help(&self) -> Vec<Vec<&Binding>>
Returns all key bindings organized into logical groups.
Provides comprehensive help information with bindings grouped by functionality. The grouping helps users understand related actions and discover advanced features.
§Binding Groups
- Cursor movement: Up/down navigation
- Page navigation: Page up/down, home/end
- Filtering: Start filter, clear filter, accept
- Help and quit: Show help, quit application
Source§impl<I: Item + Send + Sync + 'static> Model for Model<I>
impl<I: Item + Send + Sync + 'static> Model for Model<I>
Source§fn init() -> (Self, Option<Cmd>)
fn init() -> (Self, Option<Cmd>)
Initializes a new empty list model with default settings.
This creates a new list with no items, using the default delegate and standard dimensions. This method is called by the BubbleTea runtime when the model is first created.
§Returns
A tuple containing:
- The initialized list model with default settings
None(no initial command to execute)
§Default Configuration
- Empty items list
DefaultDelegatefor rendering- 80 columns × 24 rows dimensions
- Default styling and key bindings
Source§fn update(&mut self, msg: Msg) -> Option<Cmd>
fn update(&mut self, msg: Msg) -> Option<Cmd>
Handles keyboard input and state updates.
This method processes all user input and updates the list state accordingly. It implements different input handling modes based on the current filtering state:
§While Filtering (FilterState::Filtering)
- Escape: Cancel filtering, return to previous state
- Enter: Accept current filter, change to
FilterAppliedstate - Characters: Add to filter text, update results in real-time
- Backspace: Remove characters from filter
- Arrow keys: Navigate filter input cursor position
§Normal Navigation (other states)
- Up/Down: Move cursor through items with smooth viewport scrolling
- Home/End: Jump to first/last item
- / : Start filtering mode
- Ctrl+C: Clear any active filter
§Viewport Management
The update method automatically manages viewport scrolling to ensure the cursor remains visible when navigating through items.
Source§fn view(&self) -> String
fn view(&self) -> String
Renders the complete list view as a formatted string.
This method combines all visual components of the list into a single string suitable for terminal display. The layout adapts based on the current filtering state and available content.
§Returns
A formatted string containing the complete list UI with ANSI styling codes.
§Layout Structure
The view consists of three vertically stacked sections:
-
Header: Title or filter input (depending on state)
- Normal: “List Title” or “List Title (filtered: N)”
- Filtering: “Filter: > user_input”
-
Items: The main content area showing visible items
- Styled according to the current delegate
- Shows “No items” message when empty
- Viewport-based rendering for large lists
-
Footer: Status and help information
- Status: “X/Y items” format
- Help: Context-sensitive key bindings
§Performance
The view method only renders items currently visible in the viewport, ensuring consistent performance regardless of total item count.
§Examples
let list = Model::new(
vec![DefaultItem::new("Item 1", "Description")],
DefaultDelegate::new(),
80, 24
);
let output = list.view();
// Contains formatted list with title, items, and status barAuto Trait Implementations§
impl<I> Freeze for Model<I>
impl<I> !RefUnwindSafe for Model<I>
impl<I> Send for Model<I>where
I: Send,
impl<I> !Sync for Model<I>
impl<I> Unpin for Model<I>where
I: Unpin,
impl<I> !UnwindSafe for Model<I>
Blanket Implementations§
Source§impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
Source§fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
Source§fn adapt_into(self) -> D
fn adapt_into(self) -> D
Source§impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
Source§fn arrays_from(colors: C) -> T
fn arrays_from(colors: C) -> T
Source§impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
Source§fn arrays_into(self) -> C
fn arrays_into(self) -> C
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
Source§type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
parameters when converting.Source§fn cam16_into_unclamped(
self,
parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>,
) -> T
fn cam16_into_unclamped( self, parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>, ) -> T
self into C, using the provided parameters.Source§impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
Source§fn components_from(colors: C) -> T
fn components_from(colors: C) -> T
Source§impl<T> FromAngle<T> for T
impl<T> FromAngle<T> for T
Source§fn from_angle(angle: T) -> T
fn from_angle(angle: T) -> T
angle.Source§impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
Source§fn from_stimulus(other: U) -> T
fn from_stimulus(other: U) -> T
other into Self, while performing the appropriate scaling,
rounding and clamping.Source§impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
Source§fn into_angle(self) -> U
fn into_angle(self) -> U
T.Source§impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
Source§type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
parameters when converting.Source§fn into_cam16_unclamped(
self,
parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>,
) -> T
fn into_cam16_unclamped( self, parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>, ) -> T
self into C, using the provided parameters.Source§impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
Source§fn into_color(self) -> U
fn into_color(self) -> U
Source§impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
Source§fn into_color_unclamped(self) -> U
fn into_color_unclamped(self) -> U
Source§impl<T> IntoStimulus<T> for T
impl<T> IntoStimulus<T> for T
Source§fn into_stimulus(self) -> T
fn into_stimulus(self) -> T
self into T, while performing the appropriate scaling,
rounding and clamping.Source§impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
Source§type Error = <C as TryFromComponents<T>>::Error
type Error = <C as TryFromComponents<T>>::Error
try_into_colors fails to cast.Source§fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
Source§impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
Source§fn try_into_color(self) -> Result<U, OutOfBounds<U>>
fn try_into_color(self) -> Result<U, OutOfBounds<U>>
OutOfBounds error is returned which contains
the unclamped color. Read more