lib: Add scrollable paragraph widget

This commit is contained in:
Erik Kundt
2024-04-02 13:01:56 +02:00
parent 52b47a7cd5
commit edcd0ff3c4
2 changed files with 138 additions and 0 deletions
+1
View File
@@ -1,5 +1,6 @@
pub mod container;
pub mod input;
pub mod text;
use std::cmp;
use std::fmt::Debug;
+137
View File
@@ -0,0 +1,137 @@
use tokio::sync::mpsc::UnboundedSender;
use termion::event::Key;
use ratatui::backend::Backend;
use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::text::Text;
use ratatui::widgets::{Block, BorderType, Borders};
use crate::flux::ui::theme::style;
use super::{Render, Widget};
pub struct ParagraphProps<'a> {
pub content: Text<'a>,
pub focus: bool,
pub has_header: bool,
pub has_footer: bool,
}
pub struct Paragraph<A> {
/// Sending actions to the state store
pub action_tx: UnboundedSender<A>,
/// Internal offset
offset: usize,
/// Internal progress
progress: usize,
}
impl<A> Paragraph<A> {
pub fn scroll(&self) -> (u16, u16) {
(self.offset as u16, 0)
}
pub fn prev(&mut self, len: usize, page_size: usize) -> (u16, u16) {
self.offset = self.offset.saturating_sub(1);
self.progress = Self::scroll_percent(self.offset, len, page_size);
self.scroll()
}
pub fn next(&mut self, len: usize, page_size: usize) -> (u16, u16) {
if self.progress < 100 {
self.offset = self.offset.saturating_add(1);
self.progress = Self::scroll_percent(self.offset, len, page_size);
}
self.scroll()
}
pub fn prev_page(&mut self, len: usize, page_size: usize) -> (u16, u16) {
self.offset = self.offset.saturating_sub(page_size);
self.progress = Self::scroll_percent(self.offset, len, page_size);
self.scroll()
}
pub fn next_page(&mut self, len: usize, page_size: usize) -> (u16, u16) {
let end = len.saturating_sub(page_size);
self.offset = std::cmp::min(self.offset.saturating_add(page_size), end);
self.progress = Self::scroll_percent(self.offset, len, page_size);
self.scroll()
}
pub fn begin(&mut self, len: usize, page_size: usize) -> (u16, u16) {
self.offset = 0;
self.progress = Self::scroll_percent(self.offset, len, page_size);
self.scroll()
}
pub fn end(&mut self, len: usize, page_size: usize) -> (u16, u16) {
self.offset = len.saturating_sub(page_size);
self.progress = Self::scroll_percent(self.offset, len, page_size);
self.scroll()
}
pub fn progress(&self) -> usize {
self.progress
}
fn scroll_percent(offset: usize, len: usize, height: usize) -> usize {
if height >= len {
100
} else {
let y = offset as f64;
let h = height as f64;
let t = len.saturating_sub(1) as f64;
let v = y / (t - h) * 100_f64;
std::cmp::max(0, std::cmp::min(100, v as usize))
}
}
}
impl<S, A> Widget<S, A> for Paragraph<A> {
fn new(state: &S, action_tx: UnboundedSender<A>) -> Self
where
Self: Sized,
{
Self {
action_tx: action_tx.clone(),
offset: 0,
progress: 0,
}
.move_with_state(state)
}
fn move_with_state(self, _state: &S) -> Self
where
Self: Sized,
{
Self { ..self }
}
fn name(&self) -> &str {
"paragraph"
}
fn handle_key_event(&mut self, _key: Key) {}
}
impl<'a, A> Render<ParagraphProps<'a>> for Paragraph<A> {
fn render<B: Backend>(&self, frame: &mut ratatui::Frame, area: Rect, props: ParagraphProps) {
let block = Block::default()
.borders(Borders::LEFT | Borders::RIGHT)
.border_type(BorderType::Rounded)
.border_style(style::border(props.focus));
frame.render_widget(block, area);
let [content_area] = Layout::horizontal([Constraint::Min(1)])
.horizontal_margin(2)
.areas(area);
let content =
ratatui::widgets::Paragraph::new(props.content.clone()).scroll((self.offset as u16, 0));
frame.render_widget(content, content_area);
}
}