1use std::rc::Rc;
24
25use gpui::prelude::*;
26use gpui::{div, px, AnyElement, App, ClickEvent, IntoElement, SharedString, Window};
27
28use super::Modal;
29use crate::button::Button;
30use crate::style::Variant;
31use crate::text::Text;
32use crate::theme::{theme, ColorName, Size};
33
34type Handler = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
35
36#[derive(IntoElement)]
39pub struct ConfirmModal {
40 title: Option<SharedString>,
41 message: Option<SharedString>,
42 children: Vec<AnyElement>,
43 confirm_label: SharedString,
44 cancel_label: SharedString,
45 danger: bool,
46 width: Option<f32>,
47 on_confirm: Option<Handler>,
48 on_cancel: Option<Handler>,
49}
50
51impl ConfirmModal {
52 pub fn new() -> Self {
53 ConfirmModal {
54 title: None,
55 message: None,
56 children: Vec::new(),
57 confirm_label: SharedString::new_static("Confirm"),
58 cancel_label: SharedString::new_static("Cancel"),
59 danger: false,
60 width: None,
61 on_confirm: None,
62 on_cancel: None,
63 }
64 }
65
66 pub fn title(mut self, title: impl Into<SharedString>) -> Self {
67 self.title = Some(title.into());
68 self
69 }
70
71 pub fn message(mut self, message: impl Into<SharedString>) -> Self {
73 self.message = Some(message.into());
74 self
75 }
76
77 pub fn confirm_label(mut self, label: impl Into<SharedString>) -> Self {
79 self.confirm_label = label.into();
80 self
81 }
82
83 pub fn cancel_label(mut self, label: impl Into<SharedString>) -> Self {
85 self.cancel_label = label.into();
86 self
87 }
88
89 pub fn danger(mut self) -> Self {
91 self.danger = true;
92 self
93 }
94
95 pub fn width(mut self, width: f32) -> Self {
97 self.width = Some(width);
98 self
99 }
100
101 pub fn on_confirm(
103 mut self,
104 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
105 ) -> Self {
106 self.on_confirm = Some(Rc::new(handler));
107 self
108 }
109
110 pub fn on_cancel(
112 mut self,
113 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
114 ) -> Self {
115 self.on_cancel = Some(Rc::new(handler));
116 self
117 }
118}
119
120impl Default for ConfirmModal {
121 fn default() -> Self {
122 ConfirmModal::new()
123 }
124}
125
126impl ParentElement for ConfirmModal {
127 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
128 self.children.extend(elements);
129 }
130}
131
132impl RenderOnce for ConfirmModal {
133 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
134 let t = theme(cx);
135 let gap = t.spacing(Size::Sm);
136
137 let mut modal = Modal::new();
138 if let Some(width) = self.width {
139 modal = modal.width(width);
140 }
141 if let Some(title) = self.title {
142 modal = modal.title(title);
143 }
144 if let Some(cancel) = self.on_cancel.clone() {
145 modal = modal.on_close(move |ev, window, cx| cancel(ev, window, cx));
146 }
147 if let Some(message) = self.message {
148 modal = modal.child(Text::new(message).dimmed().size(Size::Sm));
149 }
150 modal = modal.children(self.children);
151
152 let mut cancel_button =
153 Button::new("guise-confirm-cancel", self.cancel_label).variant(Variant::Default);
154 if let Some(handler) = self.on_cancel {
155 cancel_button = cancel_button.on_click(move |ev, window, cx| handler(ev, window, cx));
156 }
157
158 let mut confirm_button = Button::new("guise-confirm-accept", self.confirm_label);
159 if self.danger {
160 confirm_button = confirm_button.color(ColorName::Red);
161 }
162 if let Some(handler) = self.on_confirm {
163 confirm_button = confirm_button.on_click(move |ev, window, cx| handler(ev, window, cx));
164 }
165
166 modal.child(
167 div()
168 .flex()
169 .justify_end()
170 .gap(px(gap))
171 .child(cancel_button)
172 .child(confirm_button),
173 )
174 }
175}