Skip to main content

stynx_code_tui/widgets/
toast.rs

1use ratatui::{
2    buffer::Buffer,
3    layout::Rect,
4    style::{Modifier, Style},
5    text::{Line, Span},
6    widgets::{Clear, Paragraph, Widget},
7};
8
9use crate::state::{ToastKind, ToastState};
10use crate::theme;
11
12pub struct ToastStack<'a> {
13    pub state: &'a ToastState,
14}
15
16impl<'a> ToastStack<'a> {
17    pub fn new(state: &'a ToastState) -> Self { Self { state } }
18}
19
20impl<'a> Widget for ToastStack<'a> {
21    fn render(self, area: Rect, buf: &mut Buffer) {
22        if self.state.items.is_empty() {
23            return;
24        }
25
26        let max_w = 50.min(area.width.saturating_sub(4)) as usize;
27        let mut y = area.y + 1;
28        let right_edge = area.x + area.width.saturating_sub(2);
29
30        for toast in &self.state.items {
31            if y >= area.y + area.height { break; }
32
33            let (icon, color) = match toast.kind {
34                ToastKind::Info => ("ⓘ", theme::ACCENT()),
35                ToastKind::Success => ("✓", theme::SUCCESS()),
36                ToastKind::Warning => ("△", theme::WARNING()),
37                ToastKind::Error => ("✗", theme::ERROR()),
38            };
39
40            let trimmed: String = if toast.message.chars().count() > max_w.saturating_sub(4) {
41                let mut s: String = toast.message.chars().take(max_w.saturating_sub(5)).collect();
42                s.push('…');
43                s
44            } else {
45                toast.message.clone()
46            };
47
48            let text = format!(" {icon}  {trimmed} ");
49            let width = text.chars().count() as u16;
50            let x = right_edge.saturating_sub(width);
51
52            let rect = Rect { x, y, width, height: 1 };
53            Clear.render(rect, buf);
54
55            let line = Line::from(vec![
56                Span::styled(format!(" {icon}  "), Style::default().fg(color).bg(theme::BACKGROUND_ELEMENT()).add_modifier(Modifier::BOLD)),
57                Span::styled(format!("{trimmed} "), Style::default().fg(theme::TEXT()).bg(theme::BACKGROUND_ELEMENT())),
58            ]);
59            Paragraph::new(line)
60                .style(Style::default().bg(theme::BACKGROUND_ELEMENT()))
61                .render(rect, buf);
62
63            y += 1;
64        }
65    }
66}