Skip to main content

hefesto_widgets/popups/themed_confirmation_popup/
mod.rs

1use ratatui::{
2    buffer::Buffer,
3    layout::Rect,
4    text::Line,
5    style::{Color, Style},
6    widgets::Widget,
7};
8
9use crate::{BadgeStack, BorderType, ConfirmationPopup, DotGridConfig, DotPattern, PopupSize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ConfirmationVariant {
13    Success,
14    Warning,
15    Danger,
16    None,
17}
18
19#[derive(Clone)]
20pub struct ThemedConfirmationPopup<'a> {
21    title: &'a str,
22    body: Vec<Line<'a>>,
23    variant: ConfirmationVariant,
24    confirm_label: &'a str,
25    cancel_label: &'a str,
26    border_type: BorderType,
27    position: Option<(u16, u16)>,
28    origin: Option<(u16, u16)>,
29    width: Option<PopupSize>,
30    height: Option<PopupSize>,
31    header: bool,
32    padding: u16,
33    bg_color: Option<Color>,
34    no_dot_grid: bool,
35    dot_grid: Option<DotGridConfig>,
36    badges: Option<BadgeStack<'a>>,
37}
38
39impl<'a> ThemedConfirmationPopup<'a> {
40    fn border_color(variant: ConfirmationVariant) -> Color {
41        match variant {
42            ConfirmationVariant::Success => Color::Green,
43            ConfirmationVariant::Warning => Color::Yellow,
44            ConfirmationVariant::Danger => Color::Red,
45            ConfirmationVariant::None => Color::Gray,
46        }
47    }
48
49    fn confirm_color(variant: ConfirmationVariant) -> Color {
50        match variant {
51            ConfirmationVariant::Success => Color::Green,
52            ConfirmationVariant::Warning => Color::Yellow,
53            ConfirmationVariant::Danger => Color::Red,
54            ConfirmationVariant::None => Color::Blue,
55        }
56    }
57
58    fn build_inner(&self) -> ConfirmationPopup<'a> {
59        let mut popup = ConfirmationPopup::new()
60            .title(self.title)
61            .body(self.body.clone())
62            .border_color(Self::border_color(self.variant))
63            .confirm_style(Style::new().bg(Self::confirm_color(self.variant)).fg(Color::Black))
64            .cancel_style(Style::new().bg(Color::Gray).fg(Color::Black))
65            .border_type(self.border_type)
66            .options(self.confirm_label, self.cancel_label)
67            .padding(self.padding);
68
69        if let Some((x, y)) = self.position {
70            popup = popup.position(x, y);
71        }
72        if let Some((x, y)) = self.origin {
73            popup = popup.origin(x, y);
74        }
75        if let Some(w) = self.width {
76            popup = popup.width(w);
77        }
78        if let Some(h) = self.height {
79            popup = popup.height(h);
80        }
81        if self.header {
82            popup = popup.header();
83        }
84        if let Some(bg) = self.bg_color {
85            popup = popup.bg_color(bg);
86        }
87        if self.no_dot_grid {
88            popup = popup.no_background();
89        }
90        if let Some(ref dg) = self.dot_grid {
91            popup = popup
92                .background_dots(dg.color, &dg.symbol, dg.density_x)
93                .background_spacing(dg.density_x, dg.density_y);
94        }
95        if let Some(ref badges) = self.badges {
96            popup = popup.badges(badges.clone());
97        }
98
99        popup
100    }
101
102    pub fn new(title: &'a str, body: Vec<Line<'a>>, variant: ConfirmationVariant) -> Self {
103        Self {
104            title,
105            body,
106            variant,
107            confirm_label: "Confirmar",
108            cancel_label: "Cancelar",
109            border_type: BorderType::Rounded,
110            position: None,
111            origin: None,
112            width: None,
113            height: None,
114            header: false,
115            padding: 0,
116            bg_color: None,
117            no_dot_grid: false,
118            dot_grid: None,
119            badges: None,
120        }
121    }
122
123    pub fn options(mut self, confirm_label: &'a str, cancel_label: &'a str) -> Self {
124        self.confirm_label = confirm_label;
125        self.cancel_label = cancel_label;
126        self
127    }
128
129    pub fn border_type(mut self, border_type: BorderType) -> Self {
130        self.border_type = border_type;
131        self
132    }
133
134    pub fn position(mut self, x: u16, y: u16) -> Self {
135        self.position = Some((x, y));
136        self
137    }
138
139    pub fn origin(mut self, x: u16, y: u16) -> Self {
140        self.origin = Some((x, y));
141        self
142    }
143
144    pub fn width(mut self, width: PopupSize) -> Self {
145        self.width = Some(width);
146        self
147    }
148
149    pub fn height(mut self, height: PopupSize) -> Self {
150        self.height = Some(height);
151        self
152    }
153
154    pub fn header(mut self) -> Self {
155        self.header = true;
156        self
157    }
158
159    pub fn padding(mut self, padding: u16) -> Self {
160        self.padding = padding;
161        self
162    }
163
164    pub fn resolve_rect(&self, area: Rect) -> Rect {
165        self.build_inner().resolve_rect(area)
166    }
167
168    /// Background color for the entire popup area (including border).
169    /// Delegates to `ConfirmationPopup::bg_color` → `Popup::bg_color`.
170    pub fn bg_color(mut self, color: Color) -> Self {
171        self.bg_color = Some(color);
172        self
173    }
174
175    pub fn no_background(mut self) -> Self {
176        self.no_dot_grid = true;
177        self
178    }
179
180    pub fn background_dots(mut self, color: Color, symbol: &str, density: u16) -> Self {
181        self.dot_grid = Some(DotGridConfig { color, symbol: symbol.to_string(), density_x: density, density_y: density, pattern: DotPattern::default() });
182        self
183    }
184
185    pub fn background_pattern(mut self, pattern: DotPattern) -> Self {
186        if let Some(ref mut dg) = self.dot_grid {
187            dg.pattern = pattern;
188        } else {
189            self.no_dot_grid = false;
190            self.dot_grid = Some(DotGridConfig { pattern, ..DotGridConfig::default() });
191        }
192        self
193    }
194
195    pub fn background_spacing(mut self, density_x: u16, density_y: u16) -> Self {
196        if let Some(ref mut dg) = self.dot_grid {
197            dg.density_x = density_x;
198            dg.density_y = density_y;
199        } else {
200            self.no_dot_grid = false;
201            self.dot_grid = Some(DotGridConfig {
202                density_x,
203                density_y,
204                ..DotGridConfig::default()
205            });
206        }
207        self
208    }
209
210    pub fn badges(mut self, badges: BadgeStack<'a>) -> Self {
211        self.badges = Some(badges);
212        self
213    }
214}
215
216impl Widget for ThemedConfirmationPopup<'_> {
217    fn render(self, area: Rect, buf: &mut Buffer) {
218        self.build_inner().render(area, buf);
219    }
220}
221
222#[cfg(test)]
223mod tests;