use ratatui::{
layout::Rect,
prelude::Stylize,
style::{Color, Style},
symbols::scrollbar,
widgets::{
Block, BorderType, Borders, Padding, Scrollbar, ScrollbarOrientation, ScrollbarState,
},
};
use uuid::Uuid;
pub struct AppBlock {
#[allow(dead_code)]
id: Uuid,
title: Option<String>,
lines_count: usize,
scroll_position: usize,
scrollbar_state: ScrollbarState,
padding: Option<Padding>,
horizontal_scroll_position: usize,
horizontal_scrollbar_state: ScrollbarState,
content_width: usize,
}
impl AppBlock {
pub fn new() -> Self {
Self {
id: Uuid::new_v4(),
title: None,
lines_count: 0,
scroll_position: 0,
scrollbar_state: ScrollbarState::default(),
padding: None,
horizontal_scroll_position: 0,
horizontal_scrollbar_state: ScrollbarState::default(),
content_width: 0,
}
}
pub fn set_title(mut self, title: impl Into<String>) -> Self {
self.update_title(title);
self
}
pub fn set_padding(mut self, padding: Padding) -> Self {
self.padding = Some(padding);
self
}
pub fn update_title(&mut self, title: impl Into<String>) {
self.title = Some(format!("─{}", title.into()));
}
#[allow(dead_code)]
pub fn id(&self) -> Uuid {
self.id
}
pub fn build(&self, focused: bool) -> Block<'_> {
let mut block = Block::default()
.borders(Borders::TOP | Borders::LEFT)
.border_type(BorderType::Rounded);
if focused {
block = block.border_style(Style::new().fg(Color::White));
} else {
block = block.border_style(Style::new().fg(Color::Gray));
}
if let Some(title) = &self.title {
let title_style = if focused {
Style::new().bold()
} else {
Style::new()
};
block = block.title(
ratatui::prelude::Line::from(title.as_str())
.style(title_style)
.left_aligned(),
);
}
if let Some(padding) = self.padding {
block = block.padding(padding);
}
block
}
pub fn update_scrollbar_state(&mut self, total_items: usize, selected_index: Option<usize>) {
if total_items > 0 {
let position = selected_index.unwrap_or(0);
self.scrollbar_state = self
.scrollbar_state
.content_length(total_items)
.position(position);
} else {
self.scrollbar_state = self.scrollbar_state.content_length(1).position(0);
}
}
pub fn set_lines_count(&mut self, lines_count: usize) {
self.lines_count = lines_count;
}
pub fn get_lines_count(&self) -> usize {
self.lines_count
}
pub fn set_scroll_position(&mut self, scroll_position: usize) {
self.scroll_position = scroll_position;
}
pub fn get_scroll_position(&self) -> usize {
self.scroll_position
}
pub fn get_scrollbar_state(&mut self) -> &mut ScrollbarState {
&mut self.scrollbar_state
}
pub fn set_horizontal_scroll_position(&mut self, position: usize) {
self.horizontal_scroll_position = position;
}
pub fn get_horizontal_scroll_position(&self) -> usize {
self.horizontal_scroll_position
}
pub fn get_content_width(&self) -> usize {
self.content_width
}
pub fn update_horizontal_scrollbar_state(
&mut self,
content_width: usize,
viewport_width: usize,
) {
self.content_width = content_width;
if content_width > viewport_width {
let position = self
.horizontal_scroll_position
.min(content_width.saturating_sub(viewport_width));
self.horizontal_scroll_position = position;
self.horizontal_scrollbar_state = self
.horizontal_scrollbar_state
.content_length(content_width.saturating_sub(viewport_width))
.position(position);
} else {
self.horizontal_scroll_position = 0;
self.horizontal_scrollbar_state = self
.horizontal_scrollbar_state
.content_length(1)
.position(0);
}
}
pub fn get_horizontal_scrollbar_state(&mut self) -> &mut ScrollbarState {
&mut self.horizontal_scrollbar_state
}
pub fn create_scrollbar(focused: bool) -> Scrollbar<'static> {
let color = if focused { Color::White } else { Color::Gray };
Scrollbar::new(ScrollbarOrientation::VerticalRight)
.symbols(scrollbar::VERTICAL)
.style(Style::default().fg(color))
.begin_symbol(Some("╮"))
.end_symbol(Some("╯"))
.track_symbol(Some("│"))
.thumb_symbol("█")
}
pub fn create_horizontal_scrollbar(focused: bool) -> Scrollbar<'static> {
let color = if focused { Color::White } else { Color::Gray };
Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
.symbols(scrollbar::HORIZONTAL)
.style(Style::default().fg(color))
.begin_symbol(Some("╰"))
.end_symbol(Some("─"))
.track_symbol(Some("─"))
.thumb_symbol("🬋")
}
pub fn create_horizontal_track_only(focused: bool) -> Scrollbar<'static> {
let color = if focused { Color::White } else { Color::Gray };
Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
.symbols(scrollbar::HORIZONTAL)
.style(Style::default().fg(color))
.begin_symbol(Some("╰"))
.end_symbol(Some("─"))
.track_symbol(Some("─"))
.thumb_symbol("─") }
pub fn get_content_rect(&self, area: Rect, focused: bool) -> Rect {
self.build(focused).inner(area)
}
}
impl Default for AppBlock {
fn default() -> Self {
Self::new()
}
}