glassy_ui/
alert_dialog.rs1use std::rc::Rc;
2
3use gpui::{prelude::*, App, FocusHandle, RenderOnce, SharedString, Window};
4
5use crate::{
6 Button, ButtonVariant, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader,
7 DialogTitle,
8};
9
10type AlertDialogHandler = Rc<dyn Fn(&mut Window, &mut App) + 'static>;
11
12struct AlertDialogState {
13 cancel_focus: FocusHandle,
14 confirm_focus: FocusHandle,
15}
16
17#[derive(IntoElement)]
19pub struct AlertDialog {
20 id: SharedString,
21 open: bool,
22 title: SharedString,
23 description: SharedString,
24 cancel_label: SharedString,
25 confirm_label: SharedString,
26 on_cancel: Option<AlertDialogHandler>,
27 on_confirm: Option<AlertDialogHandler>,
28}
29
30impl AlertDialog {
31 pub fn new(
32 id: impl Into<SharedString>,
33 title: impl Into<SharedString>,
34 description: impl Into<SharedString>,
35 ) -> Self {
36 Self {
37 id: id.into(),
38 open: false,
39 title: title.into(),
40 description: description.into(),
41 cancel_label: SharedString::from("Cancel"),
42 confirm_label: SharedString::from("Continue"),
43 on_cancel: None,
44 on_confirm: None,
45 }
46 }
47
48 pub fn open(mut self, open: bool) -> Self {
49 self.open = open;
50 self
51 }
52
53 pub fn cancel_label(mut self, label: impl Into<SharedString>) -> Self {
54 self.cancel_label = label.into();
55 self
56 }
57
58 pub fn confirm_label(mut self, label: impl Into<SharedString>) -> Self {
59 self.confirm_label = label.into();
60 self
61 }
62
63 pub fn on_cancel(mut self, listener: impl Fn(&mut Window, &mut App) + 'static) -> Self {
64 self.on_cancel = Some(Rc::new(listener));
65 self
66 }
67
68 pub fn on_confirm(mut self, listener: impl Fn(&mut Window, &mut App) + 'static) -> Self {
69 self.on_confirm = Some(Rc::new(listener));
70 self
71 }
72}
73
74impl RenderOnce for AlertDialog {
75 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
76 let state_id = SharedString::from(format!("{}-actions", self.id));
77 let state = window.use_keyed_state(state_id, cx, |_, cx| AlertDialogState {
78 cancel_focus: cx.focus_handle().tab_stop(true),
79 confirm_focus: cx.focus_handle().tab_stop(true),
80 });
81 let cancel_focus = state.read(cx).cancel_focus.clone();
82 let confirm_focus = state.read(cx).confirm_focus.clone();
83 let cancel_click = self.on_cancel.clone();
84 let confirm_click = self.on_confirm.clone();
85 let escape_cancel = self.on_cancel;
86 let cancel_id = SharedString::from(format!("{}-cancel", self.id));
87 let confirm_id = SharedString::from(format!("{}-confirm", self.id));
88
89 Dialog::new(self.id)
90 .open(self.open)
91 .dismiss_on_scrim(false)
92 .initial_focus(cancel_focus.clone())
93 .focus_cycle([cancel_focus.clone(), confirm_focus.clone()])
94 .on_dismiss(move |window, cx| {
95 if let Some(on_cancel) = escape_cancel.as_ref() {
96 on_cancel(window, cx);
97 }
98 })
99 .child(
100 DialogContent::new()
101 .child(
102 DialogHeader::new()
103 .child(DialogTitle::new(self.title))
104 .child(DialogDescription::new(self.description)),
105 )
106 .child(
107 DialogFooter::new()
108 .child(
109 Button::new(cancel_id, self.cancel_label)
110 .variant(ButtonVariant::Ghost)
111 .focus_handle(cancel_focus)
112 .on_click(move |_, window, cx| {
113 if let Some(on_cancel) = cancel_click.as_ref() {
114 on_cancel(window, cx);
115 }
116 }),
117 )
118 .child(
119 Button::new(confirm_id, self.confirm_label)
120 .variant(ButtonVariant::Destructive)
121 .focus_handle(confirm_focus)
122 .on_click(move |_, window, cx| {
123 if let Some(on_confirm) = confirm_click.as_ref() {
124 on_confirm(window, cx);
125 }
126 }),
127 ),
128 ),
129 )
130 }
131}