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>
impl<'a> MarkdownWidget<'a>
Sourcepub fn from_state(content: &'a str, state: &'a mut MarkdownState) -> Self
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 (passstate.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>
impl<'a> MarkdownWidget<'a>
Sourcepub fn with_has_pane(self, has_pane: bool) -> Self
pub fn with_has_pane(self, has_pane: bool) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub 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
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 renderscroll- Scroll state (position, viewport, current line)source- Content source statecache- Render cache statedisplay- Display settings (line numbers, themes)collapse- Section collapse stateexpandable- Expandable content stategit_stats_state- Git stats statevim- Vim keybinding stateselection- Selection state for text selection/copydouble_click- Double-click state for detection
§Returns
A new MarkdownWidget instance.
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn with_pane_title(self, title: impl Into<String>) -> Self
pub fn with_pane_title(self, title: impl Into<String>) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn scrollbar_config(self, config: ScrollbarConfig) -> Self
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>
impl<'a> MarkdownWidget<'a>
Sourcepub fn selection_active(self, active: bool) -> Self
pub fn selection_active(self, active: bool) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn show_scrollbar(self, show: bool) -> Self
pub fn show_scrollbar(self, show: bool) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn show_statusline(self, show: bool) -> Self
pub fn show_statusline(self, show: bool) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn toc_config(self, config: TocConfig) -> Self
pub fn toc_config(self, config: TocConfig) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn toc_hovered(self, hovered: bool) -> Self
pub fn toc_hovered(self, hovered: bool) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn toc_scroll_offset(self, offset: usize) -> Self
pub fn toc_scroll_offset(self, offset: usize) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn with_theme(self, theme: &'a AppTheme) -> Self
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>
impl<'a> MarkdownWidget<'a>
Sourcepub fn with_toc_state(self, toc_state: &'a TocState) -> Self
pub fn with_toc_state(self, toc_state: &'a TocState) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn calculate_toc_area(&self, total_area: Rect) -> Option<Rect>
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>
impl<'a> MarkdownWidget<'a>
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn handle_key_event(&mut self, key: KeyEvent) -> MarkdownEvent
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 heightPageUp: Scroll up by viewport heightHome/gg: Go to topEnd/G: Go to bottom/: Enter filter modeEsc: Exit selection mode or filter modey: 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>
impl<'a> MarkdownWidget<'a>
Sourcepub fn handle_mouse_event(
&mut self,
event: &MouseEvent,
area: Rect,
) -> MarkdownEvent
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 eventarea- The area the widget occupies (for bounds checking)
Sourcepub fn check_pending_click(&mut self, area: Rect) -> MarkdownEvent
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)
Sourcepub fn get_line_info_at_position(
&self,
y: usize,
width: usize,
) -> Option<(usize, String, String)>
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.
Sourcepub fn set_rendered_lines(&mut self, lines: Vec<Line<'static>>)
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.
Sourcepub fn is_selection_active(&self) -> bool
pub fn is_selection_active(&self) -> bool
Check if selection mode is active.
Sourcepub fn selection(&self) -> &SelectionState
pub fn selection(&self) -> &SelectionState
Get the current selection state (for rendering).
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn handle_toc_click(&mut self, event: &MouseEvent, area: Rect) -> bool
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 eventarea- 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
}
}
}Sourcepub fn handle_toc_click_in_area(
&mut self,
event: &MouseEvent,
toc_area: Rect,
) -> bool
pub fn handle_toc_click_in_area( &mut self, event: &MouseEvent, toc_area: Rect, ) -> bool
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn handle_toc_hover(&mut self, event: &MouseEvent, area: Rect) -> bool
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
}
}
}Sourcepub fn is_toc_hovered(&self) -> bool
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.
Sourcepub fn get_toc_hovered_entry(&self) -> Option<usize>
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.
Sourcepub fn set_toc_hovered(&mut self, hovered: bool)
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.
Sourcepub fn get_toc_scroll_offset(&self) -> usize
pub fn get_toc_scroll_offset(&self) -> usize
Sourcepub fn set_toc_scroll_offset(&mut self, offset: usize)
pub fn set_toc_scroll_offset(&mut self, offset: usize)
Sourcepub fn update_toc_hovered_entry(&mut self, x: u16, y: u16, toc_area: Rect)
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 coordinatey- Mouse Y coordinatetoc_area- The TOC area rect
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn is_resizing(self, resizing: bool) -> Self
pub fn is_resizing(self, resizing: bool) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn take_last_double_click(&mut self) -> Option<(usize, String, String)>
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.
Sourcepub fn take_last_copied(&mut self) -> Option<String>
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>
impl<'a> MarkdownWidget<'a>
Sourcepub fn mode(self, mode: MarkdownWidgetMode) -> Self
pub fn mode(self, mode: MarkdownWidgetMode) -> Self
Source§impl<'a> MarkdownWidget<'a>
impl<'a> MarkdownWidget<'a>
Sourcepub fn get_state_sync(&mut self) -> WidgetStateSync
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);Sourcepub fn sync_state_back(self, state: &mut MarkdownState)
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§
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> 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<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&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
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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