Skip to main content

chatty_rs/app/ui/
question.rs

1use 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}