tui-widgets

repository·main·Indexed 42 days ago

https://github.com/ratatui/tui-widgets

A collection of specialized widgets for the Ratatui TUI framework, version 0.7.11. It provides high-level UI components including bar graphs (tui-bar-graph), large text rendering (tui-big-text), boxy text (tui-box-text), playing cards (tui-cards), visual equalizers (tui-equalizer), popups and modals (tui-popup), user input prompts (tui-prompts), QR codes (tui-qrcode), scrollbars (tui-scrollbar), and scrollable view containers (tui-scrollview).

Tokens
33.6K
Snippets
116
Records
139
Agent score
52%

What's inside tui-widgets

  1. Overview of tui-widgets

    main
    tui-widgets is a collection of specialized widgets designed for building terminal user interfaces using Ratatui. It serves as a unified workspace that combines several previously standalone crates to simplify maintenance and ease of use when combining widgets in a single project.
  2. How tui-scrollbar works

    main

    The scrollbar follows a stateless rendering pattern where your application manages the scroll state and the widget handles the visual representation. The workflow consists of three parts:

    1. State Ownership: Your application owns the content_len (total length of scrollable content), viewport_len (visible area length), and offset (current scroll position).
    2. Metrics Calculation: ScrollMetrics converts these lengths into precise thumb position and size.
    3. Rendering: The ScrollBar widget renders the track and thumb into a Buffer using fractional glyphs for high precision.

    Units for content_len, viewport_len, and offset are logical units along the scroll axis (e.g., lines or items). The ratio between viewport_len and content_len determines the thumb size.

    // Conceptual example of state ownership
    let lengths = ScrollLengths {
        content_len: 120,
        viewport_len: 30,
    };
    let offset = 45;
    // The app uses these values to drive the ScrollBar widget
  3. How tui-scrollbar works and its core concepts

    main

    The tui-scrollbar crate provides a proportional scrollbar widget designed for high-fidelity rendering and flexible integration.

    Key Concepts

    • Fractional Rendering: Unlike standard scrollbars that use full cells, tui-scrollbar uses Unicode block elements to provide 1/8th cell increments (subcells). This allows the scrollbar thumb to move smoothly and represent its size more accurately relative to the content.
    • Subcell Model: The widget operates on a SUBCELL = 8 logic. A track of height H is treated as H * 8 subcells. This enables precise calculation of thumb size and position.
    • Stateless Rendering: The widget implements Widget for &ScrollBar. It does not use Ratatui's StatefulWidget pattern. Instead, the application is responsible for owning and managing the scroll offset, content length, and viewport length.
    • Decoupled Metrics: The crate separates pure computation (calculating thumb size, position, and hit testing) from the actual rendering logic. This makes the math testable and predictable independently of the terminal buffer.
  4. Render arbitrary widgets in a Popup using KnownSizeWrapper

    main

    To render complex widgets (like a scrollable Paragraph) inside a Popup, the inner widget must implement the KnownSize trait. You can wrap any standard Ratatui widget with KnownSizeWrapper by providing its explicit width and height.

    use ratatui::prelude::*;
    use ratatui::text::{Span, Text};
    use ratatui::widgets::Paragraph;
    use tui_popup::{KnownSizeWrapper, Popup};
    
    fn render_scrollable_popup(frame: &mut Frame, scroll: u16) {
        let lines: Text = (0..10).map(|i| Span::raw(format!("Line {}", i))).collect();
        let paragraph = Paragraph::new(lines).scroll((scroll, 0));
        let sized_paragraph = KnownSizeWrapper {
            inner: paragraph,
            width: 21,
            height: 5,
        };
        let popup = Popup::new(sized_paragraph)
            .title("scroll: ↑/↓ quit: Esc")
            .style(Style::new().white().on_blue());
        frame.render_widget(popup, frame.area());
    }
  5. Available widget crates in tui-widgets

    main

    The tui-widgets workspace includes the following crates. You can use the main tui-widgets crate or pull in these components as standalone crates depending on your needs:

    • tui-widgets: The primary collection crate.
    • tui-bar-graph: Bar graph visualizations.
    • tui-big-text: Large text rendering.
    • tui-box-text: Text contained within boxes.
    • tui-cards: Card-based UI components.
    • tui-equalizer: Visual equalizer effects.
    • tui-popup: Popup/modal functionality.
    • tui-prompts: User input prompts.
    • tui-qrcode: QR code rendering.
    • tui-scrollbar: Scrollbar components.
    • tui-scrollview: Scrollable view containers.
  6. How ScrollBar metrics and rendering work

    main

    The tui-scrollbar uses a subcell rendering model to provide smooth, fractional scrolling.

    • Metrics: The widget relies on ScrollLengths { content_len, viewport_len }. The viewport_len represents the visible area, and content_len represents the total scrollable length.
    • Rendering: The widget uses a GlyphSet to render the track and thumb. It supports fractional blocks (using Unicode characters like ▔, 🮂, █) to represent positions that do not align perfectly with cell boundaries.
    • Layout: When arrows are used, the widget calculates an 'inner track area' (the total area minus the arrow cells) to ensure the thumb and track are correctly offset and aligned.
  7. How to store ScrollView for performance

    main

    If your scrollable content is expensive to rebuild or changes independently of the frame render, you should store the ScrollView instance and its ScrollViewState in your application state. This allows you to render the ScrollView by reference while the ScrollViewState maintains the current offset.

    use ratatui::prelude easily;
    use tui_scrollview::{ScrollView, ScrollViewState};
    
    struct App {
        scroll_view: ScrollView,
        scroll_view_state: ScrollViewState,
    }
    
    impl App {
        fn draw(&mut self, frame: &mut Frame) {
            frame.render_stateful_widget(
                &self.scroll_view,
                frame.area(),
                &mut self.scroll_view_state,
            );
        }
    }
  8. Install tui-qrcode

    main

    To use tui-qrcode, add both qrcode and tui-qrcode to your Cargo.toml. It is recommended to disable the default features of the qrcode crate since tui-qrcode does not require the code used for rendering QR codes to image formats.

    cargo add qrcode tui-qrcode --no-default-features
  9. Handle scrollbar mouse interaction

    main

    The tui-scrollbar crate supports mouse interaction (click, drag, and wheel) through an explicit, backend-agnostic API.

    Interaction Model

    Instead of the widget mutating its own state, it returns commands or new offsets that the developer must apply to their application state. This follows an "event in $\rightarrow$ new offset out" workflow.

    Supported Interactions:

    • Wheel: Adjusts the offset by a configurable step.
    • Left Click:
      • If clicking the thumb: Starts a drag operation.
      • If clicking the track: Performs a page up/down or jump-to-click (configurable).
    • Drag: Continuously updates the thumb position and converts it to a new offset.

    Implementation Steps for Developers

    1. Store State: Your application must store the offset, content_len, and viewport_len.
    2. Handle Events: Pass input events (e.g., PointerEvent or ScrollWheel) to the scrollbar interaction logic.
    3. Update Offset: Receive a ScrollCommand (e.g., SetOffset(usize) or Delta(isize)) or a new Option<usize> from the interaction handler and update your application's offset accordingly.
    4. Feature Gating: For easy integration with crossterm, enable the crossterm feature to use the provided adapter.