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}