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