Struct Model

Source
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 implement Item + 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.

  • 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>

Source

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
  • true if in Filtering or FilterApplied state
  • false if in Unfiltered state
§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 changes
Source

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 filtering
Source

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 set
Source§

impl<I: Item + Send + Sync + 'static> Model<I>

Source

pub fn new<D>(items: Vec<I>, delegate: D, width: usize, height: usize) -> Self
where D: ItemDelegate<I> + Send + Sync + 'static,

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 list
  • delegate - Item delegate that controls rendering and behavior
  • width - 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);
Source

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);
Source

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);
Source

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 activated
Source

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 mode
Source

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"
Source

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"));
Source

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);
}
Source

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");
Source

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);
}
Source

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 item
Source

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);
Source

pub fn is_empty(&self) -> bool

Returns whether the list has no visible items.

§Returns

true if there are no currently visible items, false otherwise.

§Examples
let list: Model<DefaultItem> = Model::new(vec![], DefaultDelegate::new(), 80, 24);
assert!(list.is_empty());

Trait Implementations§

Source§

impl<I: Item> KeyMap for Model<I>

Source§

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>>

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
  1. Cursor movement: Up/down navigation
  2. Page navigation: Page up/down, home/end
  3. Filtering: Start filter, clear filter, accept
  4. Help and quit: Show help, quit application
Source§

impl<I: Item + Send + Sync + 'static> Model for Model<I>

Source§

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
  • DefaultDelegate for rendering
  • 80 columns × 24 rows dimensions
  • Default styling and key bindings
Source§

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 FilterApplied state
  • 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

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:

  1. Header: Title or filter input (depending on state)

    • Normal: “List Title” or “List Title (filtered: N)”
    • Filtering: “Filter: > user_input”
  2. 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
  3. 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 bar

Auto 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 S
where 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) -> D
where M: TransformMatrix<T>,

Convert the source color to the destination color using the specified method.
Source§

fn adapt_into(self) -> D

Convert the source color to the destination color using the bradford method by default.
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T, C> ArraysFrom<C> for T
where C: IntoArrays<T>,

Source§

fn arrays_from(colors: C) -> T

Cast a collection of colors into a collection of arrays.
Source§

impl<T, C> ArraysInto<C> for T
where C: FromArrays<T>,

Source§

fn arrays_into(self) -> C

Cast this collection of arrays into a collection of colors.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for U
where T: FromCam16Unclamped<WpParam, U>,

Source§

type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar

The number type that’s used in parameters when converting.
Source§

fn cam16_into_unclamped( self, parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>, ) -> T

Converts self into C, using the provided parameters.
Source§

impl<T, C> ComponentsFrom<C> for T
where C: IntoComponents<T>,

Source§

fn components_from(colors: C) -> T

Cast a collection of colors into a collection of color components.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromAngle<T> for T

Source§

fn from_angle(angle: T) -> T

Performs a conversion from angle.
Source§

impl<T, U> FromStimulus<U> for T
where U: IntoStimulus<T>,

Source§

fn from_stimulus(other: U) -> T

Converts other into Self, while performing the appropriate scaling, rounding and clamping.
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> IntoAngle<U> for T
where U: FromAngle<T>,

Source§

fn into_angle(self) -> U

Performs a conversion into T.
Source§

impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for U
where T: Cam16FromUnclamped<WpParam, U>,

Source§

type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar

The number type that’s used in parameters when converting.
Source§

fn into_cam16_unclamped( self, parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>, ) -> T

Converts self into C, using the provided parameters.
Source§

impl<T, U> IntoColor<U> for T
where U: FromColor<T>,

Source§

fn into_color(self) -> U

Convert into T with values clamped to the color defined bounds Read more
Source§

impl<T, U> IntoColorUnclamped<U> for T
where U: FromColorUnclamped<T>,

Source§

fn into_color_unclamped(self) -> U

Convert into T. The resulting color might be invalid in its color space Read more
Source§

impl<T> IntoStimulus<T> for T

Source§

fn into_stimulus(self) -> T

Converts self into T, while performing the appropriate scaling, rounding and clamping.
Source§

impl<T, C> TryComponentsInto<C> for T
where C: TryFromComponents<T>,

Source§

type Error = <C as TryFromComponents<T>>::Error

The error for when try_into_colors fails to cast.
Source§

fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>

Try to cast this collection of color components into a collection of colors. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, U> TryIntoColor<U> for T
where U: TryFromColor<T>,

Source§

fn try_into_color(self) -> Result<U, OutOfBounds<U>>

Convert into T, returning ok if the color is inside of its defined range, otherwise an OutOfBounds error is returned which contains the unclamped color. Read more
Source§

impl<C, U> UintsFrom<C> for U
where C: IntoUints<U>,

Source§

fn uints_from(colors: C) -> U

Cast a collection of colors into a collection of unsigned integers.
Source§

impl<C, U> UintsInto<C> for U
where C: FromUints<U>,

Source§

fn uints_into(self) -> C

Cast this collection of unsigned integers into a collection of colors.