MarkdownWidget

Struct MarkdownWidget 

Source
pub struct MarkdownWidget<'a> { /* private fields */ }
Expand description

A scrollable, interactive markdown widget.

This widget renders markdown content with:

  • Scroll support (keyboard and mouse)
  • Click-to-highlight line selection
  • Clickable headings to collapse/expand sections
  • Clickable frontmatter to collapse/expand
  • Expandable content blocks (“Show more”/“Show less”)
  • Text selection and copy support (drag to select)
  • Double-click detection
  • Statusline showing mode and scroll position

The widget handles ALL event processing internally and returns MarkdownEvent variants so the parent application can react appropriately.

§Mouse Capture Requirement

For click events to work (line highlighting, TOC navigation, text selection), you must enable mouse capture in your terminal setup:

use crossterm::{
    event::{EnableMouseCapture, DisableMouseCapture},
    execute,
    terminal::{EnterAlternateScreen, LeaveAlternateScreen},
};

// On startup:
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;

// On cleanup:
execute!(stdout, LeaveAlternateScreen, DisableMouseCapture)?;

Without EnableMouseCapture, scroll wheel events may still work (terminal-dependent), but click events will not be received by the application.

Implementations§

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn from_state(content: &'a str, state: &'a mut MarkdownState) -> Self

Create a new MarkdownWidget from a unified MarkdownState.

This is the preferred constructor as it simplifies state management by bundling all component states into a single struct.

§Arguments
  • content - The markdown content to render (pass state.content())
  • state - The unified markdown state containing all component states
§Returns

A new MarkdownWidget instance.

§Example
use ratatui_toolkit::markdown_widget::state::MarkdownState;
use ratatui_toolkit::MarkdownWidget;

let mut state = MarkdownState::default();
state.source.set_content("# Hello World");

let content = state.content().to_string();
let widget = MarkdownWidget::from_state(&content, &mut state)
    .show_toc(true)
    .show_statusline(true);
Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn with_has_pane(self, has_pane: bool) -> Self

Set whether to wrap the widget in a Pane.

When has_pane is true (default), the widget is wrapped in a styled Pane with title, border, and padding. Set to false for raw markdown rendering.

§Arguments
  • has_pane - Whether to wrap in a Pane (default: true)
§Returns

The modified MarkdownWidget instance.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn new( content: &'a str, scroll: &'a mut ScrollState, source: &'a mut SourceState, cache: &'a mut CacheState, display: &'a DisplaySettings, collapse: &'a mut CollapseState, expandable: &'a mut ExpandableState, git_stats_state: &'a mut GitStatsState, vim: &'a mut VimState, selection: &'a mut SelectionState, double_click: &'a mut DoubleClickState, ) -> Self

Create a new MarkdownWidget with the given content and state managers.

§Arguments
  • content - The markdown content to render
  • scroll - Scroll state (position, viewport, current line)
  • source - Content source state
  • cache - Render cache state
  • display - Display settings (line numbers, themes)
  • collapse - Section collapse state
  • expandable - Expandable content state
  • git_stats_state - Git stats state
  • vim - Vim keybinding state
  • selection - Selection state for text selection/copy
  • double_click - Double-click state for detection
§Returns

A new MarkdownWidget instance.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn with_pane(self, pane: Pane<'a>) -> Self

Configure the Pane that wraps the widget.

When has_pane is true (default), the widget is wrapped in this Pane. Use this to customize the pane’s title, icon, padding, border style, etc.

§Arguments
  • pane - The Pane configuration to use
§Returns

The modified MarkdownWidget instance.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn with_pane_color(self, color: impl Into<Color>) -> Self

Set the border color for the Pane that wraps the widget.

Only used when has_pane is true (default).

§Arguments
  • color - The color to use for the pane’s border
§Returns

The modified MarkdownWidget instance.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn with_pane_title(self, title: impl Into<String>) -> Self

Set the title for the Pane that wraps the widget.

This is typically the filename being displayed. Only used when has_pane is true (default).

§Arguments
  • title - The title to display in the pane’s title bar
§Returns

The modified MarkdownWidget instance.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn scrollbar_config(self, config: ScrollbarConfig) -> Self

Set the custom scrollbar configuration.

§Arguments
  • config - The scrollbar configuration to use
§Returns

Self for method chaining.

§Example
use ratatui::style::{Color, Style};
use ratatui_toolkit::markdown_widget::extensions::scrollbar::ScrollbarConfig;

let config = ScrollbarConfig {
    thumb_style: Style::default().fg(Color::Cyan),
    ..Default::default()
};

let widget = MarkdownWidget::from_state(&content, &mut state)
    .show_custom_scrollbar(true)
    .scrollbar_config(config);
Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn selection_active(self, active: bool) -> Self

Set whether selection mode is active.

This affects the mode displayed in the statusline (Normal vs Drag).

§Arguments
  • active - Whether selection is active
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn show_scrollbar(self, show: bool) -> Self

Enable or disable the scrollbar.

§Arguments
  • show - Whether to show the scrollbar
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn show_statusline(self, show: bool) -> Self

Set whether to show the statusline.

§Arguments
  • show - Whether to show the statusline
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn show_toc(self, show: bool) -> Self

Enable or disable the TOC (Table of Contents).

When enabled, shows heading navigation in the top-right corner. Compact mode shows lines, expanded mode (on hover) shows text.

§Arguments
  • show - Whether to show the TOC
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn toc_config(self, config: TocConfig) -> Self

Set the TOC configuration.

§Arguments
  • config - The TOC configuration
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn toc_hovered(self, hovered: bool) -> Self

Set the TOC hovered state.

When hovered, the TOC expands to show heading text.

§Arguments
  • hovered - Whether the TOC is hovered
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn toc_hovered_entry(self, index: Option<usize>) -> Self

Set the hovered TOC entry index.

§Arguments
  • index - The index of the hovered entry, or None
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn toc_scroll_offset(self, offset: usize) -> Self

Set the TOC scroll offset.

§Arguments
  • offset - The scroll offset for the TOC list
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn with_theme(self, theme: &'a AppTheme) -> Self

Applies an application theme to the widget.

When a theme is applied, the widget will use theme colors for:

  • Statusline (mode colors, background, text)
  • TOC (text, active, hover, background, border colors)
  • Selection highlighting

If no theme is set, the widget falls back to default hardcoded colors.

§Arguments
  • theme - The application theme to use for styling
§Returns

Self for method chaining.

§Example
use ratatui_toolkit::{MarkdownWidget, theme::AppTheme};

let theme = AppTheme::default();
// let widget = MarkdownWidget::new(content, scroll, selection, double_click)
//     .with_theme(&theme);
Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn with_toc_state(self, toc_state: &'a TocState) -> Self

Set the TOC state for the widget.

When a TOC state is provided, the widget can use it for TOC rendering and navigation.

§Arguments
  • toc_state - The TOC state containing entries and hover information
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn calculate_scrollbar_area(&self, area: Rect) -> Option<Rect>

Calculate the scrollbar area based on the content area.

Returns Some(Rect) if the scrollbar should be shown, None otherwise.

§Arguments
  • area - The main widget area
Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn calculate_toc_area(&self, total_area: Rect) -> Option<Rect>

Calculate the TOC area based on current widget configuration.

Uses dynamic dimensions based on content:

  • Expanded mode: width fits all headers, height fits all entries
  • Compact mode: fixed width, height based on entry count and line spacing
§Arguments
  • total_area - The total area available for the widget
§Returns

Some(Rect) with the TOC area if TOC is enabled, None otherwise.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn git_stats(self, stats: GitStats) -> Self

Set the git statistics to display in the statusline.

§Arguments
  • stats - The git statistics (additions, modified, deletions)
§Returns

Self for method chaining.

Source

pub fn maybe_git_stats(self, stats: Option<GitStats>) -> Self

Set the git statistics from an optional value.

This is useful when the git stats may or may not be available, such as when fetching from a scroll manager.

§Arguments
  • stats - Optional git statistics
§Returns

Self for method chaining.

Source

pub fn git_stats_tuple( self, additions: usize, modified: usize, deletions: usize, ) -> Self

Set the git statistics from a tuple (additions, modified, deletions).

§Arguments
  • additions - Lines added
  • modified - Files/lines modified
  • deletions - Lines deleted
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn handle_key_event(&mut self, key: KeyEvent) -> MarkdownEvent

Handle a keyboard event for navigation and actions.

This method handles:

  • j / Down: Move focused line down (scrolls when near edge)
  • k / Up: Move focused line up (scrolls when near edge)
  • PageDown: Scroll down by viewport height
  • PageUp: Scroll up by viewport height
  • Home / gg: Go to top
  • End / G: Go to bottom
  • /: Enter filter mode
  • Esc: Exit selection mode or filter mode
  • y: Copy selection to clipboard (when selection active)
  • Ctrl+Shift+C: Copy selection to clipboard

Returns a MarkdownEvent indicating what action was taken.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn handle_mouse_event( &mut self, event: &MouseEvent, area: Rect, ) -> MarkdownEvent

Handle a mouse event for all interactions.

This method handles:

  • Click-to-focus: Sets the current line on click (highlights it)
  • Double-click: Returns event with line info
  • Text selection: Drag to select, auto-copy on release
  • Heading collapse: Click on heading to toggle
  • Scrolling: Mouse wheel to scroll

Returns a MarkdownEvent indicating what action was taken.

§Mouse Capture Requirement

This method requires EnableMouseCapture to be enabled for click events. Scroll events may work without it (terminal-dependent).

§Arguments
  • event - The mouse event
  • area - The area the widget occupies (for bounds checking)
Source

pub fn check_pending_click(&mut self, area: Rect) -> MarkdownEvent

Check for pending single-click timeout and process if needed.

Call this method periodically (e.g., each frame) to handle deferred single-click actions like heading collapse and focus line changes.

Returns a MarkdownEvent if a pending click was processed.

§Arguments
  • area - The area the widget occupies (for position calculations)
Source

pub fn get_line_info_at_position( &self, y: usize, width: usize, ) -> Option<(usize, String, String)>

Get line information at a given screen position.

Returns (line_number, line_kind, content) if found.

Source

pub fn set_rendered_lines(&mut self, lines: Vec<Line<'static>>)

Set the rendered lines for selection text extraction.

Call this after rendering to update the cached lines.

Source

pub fn is_selection_active(&self) -> bool

Check if selection mode is active.

Source

pub fn selection(&self) -> &SelectionState

Get the current selection state (for rendering).

Source

pub fn get_current_line_info( &self, width: usize, ) -> Option<(usize, String, String)>

Get line information at the current highlighted line.

Returns (line_number, line_kind, content) if found.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn handle_toc_click(&mut self, event: &MouseEvent, area: Rect) -> bool

Handle a click on the TOC to scroll to the selected heading.

§Arguments
  • event - The mouse event
  • area - The total widget area
§Returns

true if the click was handled (was on a TOC entry), false otherwise.

§Example
// In your event loop:
if let Event::Mouse(mouse_event) = event {
    if matches!(mouse_event.kind, MouseEventKind::Down(MouseButton::Left)) {
        if widget.handle_toc_click(&mouse_event, area) {
            // Click was handled - you may want to redraw
        }
    }
}
Source

pub fn handle_toc_click_in_area( &mut self, event: &MouseEvent, toc_area: Rect, ) -> bool

Handle a click on the TOC in a specific area (for when area is pre-calculated).

§Arguments
  • event - The mouse event
  • toc_area - The pre-calculated TOC area
§Returns

true if the click was handled (was on a TOC entry), false otherwise.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn handle_toc_hover(&mut self, event: &MouseEvent, area: Rect) -> bool

Handle mouse move events to detect TOC hover.

Call this method with MouseEventKind::Moved events to track whether the mouse is hovering over the TOC area and which entry is being hovered.

§Arguments
  • event - The mouse event (should be a Moved event)
  • area - The total widget area
§Returns

true if the hover state changed (entered/exited hover or hovered entry changed), false otherwise.

§Example
// In your event loop:
if let Event::Mouse(mouse_event) = event {
    if matches!(mouse_event.kind, MouseEventKind::Moved) {
        if widget.handle_toc_hover(&mouse_event, area) {
            // Hover state changed - you may want to redraw
        }
    }
}
Source

pub fn is_toc_hovered(&self) -> bool

Check if the TOC is currently being hovered.

§Returns

true if the mouse is over the TOC, false otherwise.

Source

pub fn get_toc_hovered_entry(&self) -> Option<usize>

Get the currently hovered TOC entry index.

§Returns

The index of the hovered entry, or None if no entry is hovered.

Source

pub fn set_toc_hovered(&mut self, hovered: bool)

Set the TOC hover state directly.

Useful for manually controlling hover state in tests or special scenarios.

§Arguments
  • hovered - Whether the TOC should be considered hovered.
Source

pub fn get_toc_scroll_offset(&self) -> usize

Get the current TOC scroll offset.

§Returns

The current scroll offset for the TOC list.

Source

pub fn set_toc_scroll_offset(&mut self, offset: usize)

Set the TOC scroll offset directly.

§Arguments
  • offset - The scroll offset for the TOC list.
Source

pub fn update_toc_hovered_entry(&mut self, x: u16, y: u16, toc_area: Rect)

Update the hovered entry based on current mouse position and scroll offset.

Call this after scrolling the TOC to recalculate which entry is under the cursor.

§Arguments
  • x - Mouse X coordinate
  • y - Mouse Y coordinate
  • toc_area - The TOC area rect
Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn is_resizing(self, resizing: bool) -> Self

Set whether the widget is currently being resized (for smoother drag performance).

§Arguments
  • resizing - Whether the widget is being resized
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn take_last_double_click(&mut self) -> Option<(usize, String, String)>

Get the last double-click info and clear it.

Call this after processing events to check if a double-click occurred.

§Returns

Some((line_number, line_kind, content)) if a double-click occurred, None otherwise.

Source

pub fn take_last_copied(&mut self) -> Option<String>

Get the last copied text and clear it.

Call this after processing events to check if text was copied to clipboard. Use this to show a toast notification when text is copied.

§Returns

Some(text) if text was copied, None otherwise.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn mode(self, mode: MarkdownWidgetMode) -> Self

Set the current mode for the statusline.

§Arguments
  • mode - The mode to display (Normal or Drag)
§Returns

Self for method chaining.

Source§

impl<'a> MarkdownWidget<'a>

Source

pub fn get_state_sync(&mut self) -> WidgetStateSync

Get the state that needs to be synced back to MarkdownState.

This method captures the TOC and selection state from the widget so it can be synced back after the widget is dropped.

§Returns

A WidgetStateSync struct containing the state values to sync.

§Example
let sync_state = {
    let mut widget = MarkdownWidget::from_state(&content, &mut state).show_toc(true);
    widget.handle_toc_hover(&mouse, render_area);
    widget.handle_toc_click(&mouse, render_area);
    widget.handle_mouse_event(&mouse, render_area);
    widget.get_state_sync()
};
sync_state.apply_to(&mut state);
Source

pub fn sync_state_back(self, state: &mut MarkdownState)

Sync widget state back to MarkdownState by consuming self.

This method consumes the widget and syncs TOC and selection state back to the MarkdownState, ensuring state persistence between frames.

Call this after handling mouse events to preserve hover and selection state.

§Arguments
  • state - The MarkdownState to sync state back to
§Example
let mut widget = MarkdownWidget::from_state(&content, &mut state).show_toc(true);
widget.handle_toc_hover(&mouse, render_area);
widget.handle_toc_click(&mouse, render_area);
widget.handle_mouse_event(&mouse, render_area);
widget.sync_state_back(&mut state);

Trait Implementations§

Source§

impl<'a> Widget for MarkdownWidget<'a>

Source§

fn render(self, area: Rect, buf: &mut Buffer)

Draws the current state of the widget in the given buffer. That is the only method required to implement a custom widget.

Auto Trait Implementations§

§

impl<'a> Freeze for MarkdownWidget<'a>

§

impl<'a> RefUnwindSafe for MarkdownWidget<'a>

§

impl<'a> Send for MarkdownWidget<'a>

§

impl<'a> Sync for MarkdownWidget<'a>

§

impl<'a> Unpin for MarkdownWidget<'a>

§

impl<'a> !UnwindSafe for MarkdownWidget<'a>

Blanket Implementations§

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> 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<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more