use crate::app::App; use ratatui::{ Frame, buffer::Buffer, layout::Rect, style::Stylize, symbols::border, text::{Line, Text}, widgets::{Block, Paragraph, Widget}, }; /// Explicitly declare lifetime as App is a reference; /// Runtime is only instantiated for render struct Runtime<'a> { app: &'a App, } pub fn draw(frame: &mut Frame, app: &App) { let run = Runtime { app }; frame.render_widget(&run, frame.area()); } impl<'a> Widget for &Runtime<'a> { fn render(self, area: Rect, buf: &mut Buffer) { let title = Line::from(" Counter App Tutorial ".bold()); let instructions = Line::from(vec![ " Decrement ".into(), "".blue().bold(), " Increment ".into(), "".blue().bold(), " Quit ".into(), " ".blue().bold(), ]); let block = Block::bordered() .title(title.centered()) .title_bottom(instructions.centered()) .border_set(border::THICK); let counter_text = Text::from(vec![Line::from(vec![ "Value: ".into(), self.app.display_counter().yellow(), ])]); Paragraph::new(counter_text) .centered() .block(block) .render(area, buf); } } #[cfg(test)] mod tests { use super::*; use ratatui::style::Style; #[test] fn render() { let mut app = App::default(); assert!(app.init_context().is_ok()); let runtime = Runtime { app: &app }; let mut buf = Buffer::empty(Rect::new(0, 0, 50, 4)); runtime.render(buf.area, &mut buf); let mut expected = Buffer::with_lines(vec![ "┏━━━━━━━━━━━━━ Counter App Tutorial ━━━━━━━━━━━━━┓", "┃ Value: 0 ┃", "┃ ┃", "┗━ Decrement Increment Quit ━━┛", ]); let title_style = Style::new().bold(); let counter_style = Style::new().yellow(); let key_style = Style::new().blue().bold(); expected.set_style(Rect::new(14, 0, 22, 1), title_style); expected.set_style(Rect::new(28, 1, 1, 1), counter_style); expected.set_style(Rect::new(13, 3, 6, 1), key_style); expected.set_style(Rect::new(30, 3, 7, 1), key_style); expected.set_style(Rect::new(43, 3, 4, 1), key_style); assert_eq!(buf, expected); } }