Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Add scrollable paragraph widget
Erik Kundt committed 2 years ago
commit edcd0ff3c4da125e834e15609314fea9fdaabdff
parent 52b47a7cd5e64d43dc2f834f286955ac9f27fd9a
2 files changed +138 -0
modified src/flux/ui/widget.rs
@@ -1,5 +1,6 @@
pub mod container;
pub mod input;
+
pub mod text;

use std::cmp;
use std::fmt::Debug;
added src/flux/ui/widget/text.rs
@@ -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);
+
    }
+
}