chatty_rs/app/ui/
question.rs1use ratatui::{
2 Frame,
3 layout::{Alignment, Rect},
4 style::{Style, Stylize},
5 text::{Line, Text},
6 widgets::{Block, BorderType, Borders, Clear, Padding},
7};
8use ratatui_macros::span;
9
10use super::utils;
11
12#[derive(Default)]
13pub struct Question<'a> {
14 showing: bool,
15 question: Line<'a>,
16 title: Option<Line<'a>>,
17}
18
19impl<'a> Question<'a> {
20 pub fn with_title(mut self, title: impl Into<Line<'a>>) -> Question<'a> {
21 self.set_title(title);
22 self
23 }
24
25 pub fn set_title(&mut self, title: impl Into<Line<'a>>) {
26 self.title = Some(title.into());
27 }
28
29 pub fn showing(&self) -> bool {
30 self.showing
31 }
32
33 pub fn open(&mut self, question: impl Into<Line<'a>>) {
34 self.question = question.into();
35 self.showing = true;
36 }
37
38 pub fn close(&mut self) {
39 self.showing = false;
40 }
41
42 pub fn render(&mut self, f: &mut Frame, area: Rect) {
43 if !self.showing {
44 return;
45 }
46
47 let max_width = (area.width as f32 * 0.8).ceil() as u16;
48 let lines = utils::split_to_lines(self.question.spans.clone(), (max_width - 2) as usize);
49 let area = build_area(area, max_width, lines.len() as u16 + 2);
50
51 let mut block = Block::default()
52 .borders(Borders::ALL)
53 .border_type(BorderType::Rounded)
54 .padding(Padding::symmetric(1, 0))
55 .title_bottom(vec![
56 span!(" "),
57 span!("q").green().bold(),
58 span!(" to close, ").white(),
59 span!("y").green().bold(),
60 span!(" to confirm, ").white(),
61 span!("n").green().bold(),
62 span!(" to cancel ").white(),
63 ])
64 .title_alignment(Alignment::Center)
65 .border_style(Style::default().light_blue());
66
67 if let Some(title) = &self.title {
68 block = block
69 .title(title.clone())
70 .title_alignment(Alignment::Center);
71 }
72
73 f.render_widget(Clear, area);
74 let inner = block.inner(area);
75 f.render_widget(block, area);
76
77 let text = Text::from(lines);
78 f.render_widget(text, inner);
79 }
80}
81
82fn build_area(area: Rect, w: u16, h: u16) -> Rect {
83 let x = area.x + (area.width - w) / 2;
84 let y = area.y + (area.height - 1) / 3;
85 Rect::new(x, y, w, h)
86}