guise/feedback/
notification.rs1use gpui::prelude::*;
4use gpui::{div, px, App, ClickEvent, FontWeight, IntoElement, SharedString, Window};
5
6use crate::devtools::Probed;
7use crate::icon::Glyph;
8use crate::input::ClickHandler;
9use crate::theme::{theme, ColorName, Size};
10
11#[derive(IntoElement)]
14pub struct Notification {
15 title: Option<SharedString>,
16 message: SharedString,
17 color: ColorName,
18 icon: Option<Glyph>,
19 on_close: Option<ClickHandler>,
20}
21
22impl Notification {
23 pub fn new(message: impl Into<SharedString>) -> Self {
24 Notification {
25 title: None,
26 message: message.into(),
27 color: ColorName::Blue,
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 color(mut self, color: ColorName) -> Self {
39 self.color = color;
40 self
41 }
42
43 pub fn icon(mut self, icon: impl Into<Glyph>) -> Self {
44 self.icon = Some(icon.into());
45 self
46 }
47
48 pub fn on_close(
49 mut self,
50 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
51 ) -> Self {
52 self.on_close = Some(Box::new(handler));
53 self
54 }
55}
56
57impl RenderOnce for Notification {
58 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
59 let t = theme(cx);
60 let accent = t.color(self.color, t.primary_shade()).hsla();
61 let surface = t.surface().hsla();
62 let text = t.text().hsla();
63 let dimmed = t.dimmed().hsla();
64 let radius = t.radius(Size::Md);
65
66 let mut content = div().flex().flex_col().gap(px(2.0)).flex_1();
67 if let Some(title) = self.title {
68 content = content.child(
69 div()
70 .font_weight(FontWeight::BOLD)
71 .text_size(px(t.font_size(Size::Sm)))
72 .text_color(text)
73 .child(title),
74 );
75 }
76 content = content.child(
77 div()
78 .text_size(px(t.font_size(Size::Sm)))
79 .text_color(dimmed)
80 .child(self.message),
81 );
82
83 let mut row = div()
84 .flex()
85 .items_start()
86 .gap(px(12.0))
87 .w(px(360.0))
88 .p(px(t.spacing(Size::Md)))
89 .rounded(px(radius))
90 .bg(surface)
91 .border_1()
92 .border_color(t.border().hsla())
93 .shadow_md()
94 .child(div().w(px(4.0)).h(px(38.0)).rounded(px(4.0)).bg(accent));
96
97 if let Some(icon) = self.icon {
98 row = row.child(
99 div()
100 .text_size(px(t.font_size(Size::Md)))
101 .text_color(accent)
102 .child(icon),
103 );
104 }
105 row = row.child(content);
106 if let Some(handler) = self.on_close {
107 row = row.child(
108 div()
109 .id("guise-notification-close")
110 .text_color(dimmed)
111 .child(SharedString::new_static("\u{00d7}"))
112 .on_click(handler),
113 );
114 }
115 row.probe("Notification")
116 }
117}