1use gpui::prelude::*;
4use gpui::{div, px, App, ClickEvent, FontWeight, IntoElement, SharedString, Window};
5
6use crate::input::ClickHandler;
7use crate::style::{surface, ColorValue, Variant};
8use crate::theme::{theme, Size};
9
10#[derive(IntoElement)]
12pub struct Alert {
13 title: Option<SharedString>,
14 message: SharedString,
15 variant: Variant,
16 color: ColorValue,
17 icon: Option<SharedString>,
18 on_close: Option<ClickHandler>,
19}
20
21impl Alert {
22 pub fn new(message: impl Into<SharedString>) -> Self {
23 Alert {
24 title: None,
25 message: message.into(),
26 variant: Variant::Light,
27 color: ColorValue::default(),
28 icon: None,
29 on_close: None,
30 }
31 }
32
33 pub fn title(mut self, title: impl Into<SharedString>) -> Self {
34 self.title = Some(title.into());
35 self
36 }
37
38 pub fn variant(mut self, variant: Variant) -> Self {
39 self.variant = variant;
40 self
41 }
42
43 pub fn color(mut self, color: impl Into<ColorValue>) -> Self {
44 self.color = color.into();
45 self
46 }
47
48 pub fn icon(mut self, icon: impl Into<SharedString>) -> Self {
50 self.icon = Some(icon.into());
51 self
52 }
53
54 pub fn on_close(
55 mut self,
56 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
57 ) -> Self {
58 self.on_close = Some(Box::new(handler));
59 self
60 }
61}
62
63impl RenderOnce for Alert {
64 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
65 let t = theme(cx);
66 let s = surface(t, self.color, self.variant);
67 let filled = self.variant == Variant::Filled;
68 let body_color = if filled { s.fg } else { t.text().hsla() };
69 let radius = t.radius(Size::Md);
70 let pad = t.spacing(Size::Md);
71
72 let mut content = div().flex().flex_col().gap(px(2.0)).flex_1();
73 if let Some(title) = self.title {
74 content = content.child(
75 div()
76 .font_weight(FontWeight::BOLD)
77 .text_size(px(t.font_size(Size::Sm)))
78 .text_color(s.fg)
79 .child(title),
80 );
81 }
82 content = content.child(
83 div()
84 .text_size(px(t.font_size(Size::Sm)))
85 .text_color(body_color)
86 .child(self.message),
87 );
88
89 let mut row = div()
90 .flex()
91 .items_start()
92 .gap(px(10.0))
93 .p(px(pad))
94 .rounded(px(radius))
95 .bg(s.bg);
96 if let Some(border) = s.border {
97 row = row.border_1().border_color(border);
98 }
99 if let Some(icon) = self.icon {
100 row = row.child(
101 div()
102 .text_size(px(t.font_size(Size::Md)))
103 .text_color(s.fg)
104 .child(icon),
105 );
106 }
107 row = row.child(content);
108 if let Some(handler) = self.on_close {
109 let close_color = if filled { s.fg } else { t.dimmed().hsla() };
110 row = row.child(
111 div()
112 .id("guise-alert-close")
113 .text_color(close_color)
114 .child(SharedString::new_static("\u{00d7}"))
115 .on_click(handler),
116 );
117 }
118 row
119 }
120}