1use gpui::prelude::*;
6use gpui::*;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum DialogSize {
11 Sm,
13 #[default]
15 Md,
16 Lg,
18 Xl,
20 Full,
22}
23
24impl DialogSize {
25 fn width(&self) -> Rems {
26 match self {
27 DialogSize::Sm => Rems(20.0),
28 DialogSize::Md => Rems(30.0),
29 DialogSize::Lg => Rems(40.0),
30 DialogSize::Xl => Rems(50.0),
31 DialogSize::Full => Rems(60.0),
32 }
33 }
34}
35
36pub struct Dialog {
38 id: ElementId,
39 title: Option<SharedString>,
40 size: DialogSize,
41 content: Option<AnyElement>,
42 footer: Option<AnyElement>,
43 show_close_button: bool,
44 close_on_backdrop: bool,
45 on_close: Option<Box<dyn Fn(&mut Window, &mut App) + 'static>>,
46}
47
48impl Dialog {
49 pub fn new(id: impl Into<ElementId>) -> Self {
51 Self {
52 id: id.into(),
53 title: None,
54 size: DialogSize::default(),
55 content: None,
56 footer: None,
57 show_close_button: true,
58 close_on_backdrop: true,
59 on_close: None,
60 }
61 }
62
63 pub fn title(mut self, title: impl Into<SharedString>) -> Self {
65 self.title = Some(title.into());
66 self
67 }
68
69 pub fn size(mut self, size: DialogSize) -> Self {
71 self.size = size;
72 self
73 }
74
75 pub fn content(mut self, element: impl IntoElement) -> Self {
77 self.content = Some(element.into_any_element());
78 self
79 }
80
81 pub fn child(self, element: impl IntoElement) -> Self {
83 self.content(element)
84 }
85
86 pub fn footer(mut self, element: impl IntoElement) -> Self {
88 self.footer = Some(element.into_any_element());
89 self
90 }
91
92 pub fn show_close_button(mut self, show: bool) -> Self {
94 self.show_close_button = show;
95 self
96 }
97
98 pub fn close_on_backdrop(mut self, close: bool) -> Self {
100 self.close_on_backdrop = close;
101 self
102 }
103
104 pub fn on_close(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
106 self.on_close = Some(Box::new(handler));
107 self
108 }
109
110 pub fn build(self) -> Div {
112 let width = self.size.width();
113 let on_close = self.on_close;
114 let close_on_backdrop = self.close_on_backdrop;
115
116 let mut backdrop = div()
118 .absolute()
119 .inset_0()
120 .flex()
121 .items_center()
122 .justify_center()
123 .bg(rgba(0x000000aa));
124
125 if close_on_backdrop {
127 if let Some(ref handler) = on_close {
128 let handler: *const dyn Fn(&mut Window, &mut App) = handler.as_ref();
129 backdrop = backdrop.on_mouse_down(MouseButton::Left, move |_event, window, cx| {
130 unsafe {
132 (*handler)(window, cx);
133 }
134 });
135 }
136 }
137
138 let mut dialog = div()
140 .id(self.id)
141 .w(width)
142 .max_h(Rems(45.0))
143 .bg(rgb(0x1e1e1e))
144 .border_1()
145 .border_color(rgb(0x007acc))
146 .rounded_lg()
147 .shadow_lg()
148 .overflow_hidden()
149 .flex()
150 .flex_col()
151 .on_mouse_down(MouseButton::Left, |_event, _window, _cx| {
153 });
155
156 if self.title.is_some() || self.show_close_button {
158 let mut header = div()
159 .flex()
160 .items_center()
161 .justify_between()
162 .px_4()
163 .py_3()
164 .border_b_1()
165 .border_color(rgb(0x3a3a3a));
166
167 if let Some(title) = self.title {
168 header = header.child(
169 div()
170 .text_lg()
171 .font_weight(FontWeight::BOLD)
172 .text_color(rgb(0xffffff))
173 .child(title),
174 );
175 } else {
176 header = header.child(div()); }
178
179 if self.show_close_button {
180 if let Some(ref handler) = on_close {
181 let handler: *const dyn Fn(&mut Window, &mut App) = handler.as_ref();
182 header = header.child(
183 div()
184 .id("dialog-close-btn")
185 .px_2()
186 .py_1()
187 .rounded(px(3.0))
188 .cursor_pointer()
189 .text_color(rgb(0x888888))
190 .hover(|s| s.bg(rgb(0x3a3a3a)).text_color(rgb(0xffffff)))
191 .on_mouse_up(MouseButton::Left, move |_event, window, cx| unsafe {
192 (*handler)(window, cx);
193 })
194 .child("×"),
195 );
196 }
197 }
198
199 dialog = dialog.child(header);
200 }
201
202 if let Some(content) = self.content {
204 dialog = dialog.child(
205 div()
206 .id("dialog-content")
207 .flex_1()
208 .overflow_y_scroll()
209 .px_4()
210 .py_4()
211 .child(content),
212 );
213 }
214
215 if let Some(footer) = self.footer {
217 dialog = dialog.child(
218 div()
219 .px_4()
220 .py_3()
221 .border_t_1()
222 .border_color(rgb(0x3a3a3a))
223 .child(footer),
224 );
225 }
226
227 backdrop.child(dialog)
228 }
229}
230
231impl IntoElement for Dialog {
232 type Element = Div;
233
234 fn into_element(self) -> Self::Element {
235 self.build()
236 }
237}