paragraph/
paragraph.rs

1use color_eyre::Result;
2use lipsum::lipsum;
3use ratatui::crossterm::event::{self, Event, KeyCode};
4use ratatui::prelude::{Rect, Span, Style, Stylize, Text};
5use ratatui::widgets::{Paragraph, Wrap};
6use ratatui::Frame;
7use tui_popup::{KnownSizeWrapper, Popup};
8
9fn main() -> Result<()> {
10    color_eyre::install()?;
11    let terminal = ratatui::init();
12    let result = App::default().run(terminal);
13    ratatui::restore();
14    result
15}
16
17#[derive(Default)]
18struct App {
19    should_exit: bool,
20    lorem_ipsum: String,
21    scroll: u16,
22}
23
24impl App {
25    fn run(&mut self, mut terminal: ratatui::DefaultTerminal) -> Result<()> {
26        self.lorem_ipsum = lipsum(2000);
27        while !self.should_exit {
28            terminal.draw(|frame| self.render(frame))?;
29            self.handle_events()?;
30        }
31        Ok(())
32    }
33
34    fn render(&self, frame: &mut Frame) {
35        let area = frame.area();
36        self.render_background(frame, area);
37        self.render_popup(frame);
38    }
39
40    fn render_background(&self, frame: &mut Frame, area: Rect) {
41        let text = Text::raw(&self.lorem_ipsum);
42        let paragraph = Paragraph::new(text).wrap(Wrap { trim: false }).dark_gray();
43        frame.render_widget(paragraph, area);
44    }
45
46    fn render_popup(&self, frame: &mut Frame) {
47        let lines: Text = (0..10).map(|i| Span::raw(format!("Line {i}"))).collect();
48        let paragraph = Paragraph::new(lines).scroll((self.scroll, 0));
49        let wrapper = KnownSizeWrapper {
50            inner: paragraph,
51            width: 21,
52            height: 5,
53        };
54        let popup = Popup::new(wrapper)
55            .title("scroll: ↑/↓ quit: Esc")
56            .style(Style::new().white().on_blue());
57        frame.render_widget(popup, frame.area());
58    }
59
60    fn handle_events(&mut self) -> Result<()> {
61        if let Event::Key(key) = event::read()? {
62            match key.code {
63                KeyCode::Char('q') | KeyCode::Esc => self.should_exit = true,
64                KeyCode::Char('j') | KeyCode::Down => self.scroll_down(),
65                KeyCode::Char('k') | KeyCode::Up => self.scroll_up(),
66                _ => {}
67            }
68        }
69        Ok(())
70    }
71
72    const fn scroll_up(&mut self) {
73        self.scroll = self.scroll.saturating_sub(1);
74    }
75
76    const fn scroll_down(&mut self) {
77        self.scroll = self.scroll.saturating_add(1);
78    }
79}