use ratatui::layout::{Margin, Rect};
use ratatui::style::{Color, Style};
use ratatui::symbols::scrollbar;
use ratatui::text::Text;
use ratatui::widgets::{
Block, Borders, List, ListItem, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState,
};
pub fn render_styled_paragraph_with_scrollbar(
f: &mut ratatui::Frame,
area: Rect,
content: Text,
scroll_position: usize,
title: &str,
is_focused: bool,
) {
let total_lines = content.lines.len();
let visible_height = area.height as usize - 2;
let max_start = total_lines.saturating_sub(visible_height);
let start = scroll_position.min(max_start);
let end = start + visible_height.min(total_lines - start);
let displayed_content = Text::from(content.lines[start..end].to_vec());
let paragraph = Paragraph::new(displayed_content).block(if is_focused {
Block::default()
.title(title)
.borders(Borders::ALL)
.border_type(ratatui::widgets::BorderType::Plain)
.border_style(Style::default().fg(Color::Yellow))
} else {
Block::default().title(title).borders(Borders::ALL)
});
f.render_widget(paragraph, area);
if total_lines > visible_height {
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
.symbols(scrollbar::VERTICAL)
.begin_symbol(None)
.track_symbol(None)
.end_symbol(None)
.style(if is_focused {
Style::default().fg(Color::Yellow)
} else {
Style::default().fg(Color::Gray)
});
let max_scroll = total_lines.saturating_sub(visible_height);
let mut scrollbar_state = ScrollbarState::default()
.content_length(max_scroll)
.viewport_content_length(visible_height)
.position(scroll_position);
f.render_stateful_widget(
scrollbar,
area.inner(Margin {
vertical: 1,
horizontal: 0,
}),
&mut scrollbar_state,
);
}
}
pub fn render_list_with_scrollbar<T>(
f: &mut ratatui::Frame,
area: Rect,
items: &[T],
selected_index: Option<usize>,
scroll_position: usize,
title: &str,
is_focused: bool,
mut item_renderer: impl FnMut(usize, &T, bool) -> ListItem,
) {
let visible_height = area.height as usize - 2; let total_items = items.len();
let max_start = total_items.saturating_sub(visible_height);
let start = scroll_position.min(max_start);
let end = start + visible_height.min(total_items - start);
let list_items: Vec<ListItem> = items[start..end]
.iter()
.enumerate()
.map(|(relative_index, item)| {
let absolute_index = start + relative_index;
let is_selected = selected_index == Some(absolute_index);
(absolute_index, item, is_selected)
})
.map(|(absolute_index, item, is_selected)| item_renderer(absolute_index, item, is_selected))
.collect();
let list = List::new(list_items).block(if is_focused {
Block::default()
.title(title)
.borders(Borders::ALL)
.border_type(ratatui::widgets::BorderType::Plain)
.border_style(Style::default().fg(Color::Yellow))
} else {
Block::default().title(title).borders(Borders::ALL)
});
f.render_widget(list, area);
if total_items > visible_height {
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
.symbols(scrollbar::VERTICAL)
.begin_symbol(None)
.track_symbol(None)
.end_symbol(None)
.style(if is_focused {
Style::default().fg(Color::Yellow)
} else {
Style::default().fg(Color::Gray)
});
let max_scroll = total_items.saturating_sub(visible_height);
let mut scrollbar_state = ScrollbarState::default()
.content_length(max_scroll)
.viewport_content_length(visible_height)
.position(scroll_position);
f.render_stateful_widget(
scrollbar,
area.inner(Margin {
vertical: 1,
horizontal: 0,
}),
&mut scrollbar_state,
);
}
}