1use editor::Editor;
2use futures::channel::oneshot;
3use gpui::{AppContext, DismissEvent, Entity, EventEmitter, Focusable, Styled};
4use ui::{
5 ActiveTheme, App, Context, DynamicSpacing, Headline, HeadlineSize, Icon, IconName, IconSize,
6 InteractiveElement, IntoElement, ParentElement, Render, SharedString, StyledExt,
7 StyledTypography, Window, div, h_flex, v_flex,
8};
9use workspace::ModalView;
10
11pub(crate) struct AskPassModal {
12 operation: SharedString,
13 prompt: SharedString,
14 editor: Entity<Editor>,
15 tx: Option<oneshot::Sender<String>>,
16}
17
18impl EventEmitter<DismissEvent> for AskPassModal {}
19impl ModalView for AskPassModal {}
20impl Focusable for AskPassModal {
21 fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
22 self.editor.focus_handle(cx)
23 }
24}
25
26impl AskPassModal {
27 pub fn new(
28 operation: SharedString,
29 prompt: SharedString,
30 tx: oneshot::Sender<String>,
31 window: &mut Window,
32 cx: &mut Context<Self>,
33 ) -> Self {
34 let editor = cx.new(|cx| {
35 let mut editor = Editor::single_line(window, cx);
36 if prompt.contains("yes/no") {
37 editor.set_masked(false, cx);
38 } else {
39 editor.set_masked(true, cx);
40 }
41 editor
42 });
43 Self {
44 operation,
45 prompt,
46 editor,
47 tx: Some(tx),
48 }
49 }
50
51 fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
52 cx.emit(DismissEvent);
53 }
54
55 fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
56 if let Some(tx) = self.tx.take() {
57 tx.send(self.editor.read(cx).text(cx)).ok();
58 }
59 cx.emit(DismissEvent);
60 }
61}
62
63impl Render for AskPassModal {
64 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
65 v_flex()
66 .key_context("PasswordPrompt")
67 .on_action(cx.listener(Self::cancel))
68 .on_action(cx.listener(Self::confirm))
69 .elevation_2(cx)
70 .size_full()
71 .font_buffer(cx)
72 .child(
73 h_flex()
74 .px(DynamicSpacing::Base12.rems(cx))
75 .pt(DynamicSpacing::Base08.rems(cx))
76 .pb(DynamicSpacing::Base04.rems(cx))
77 .rounded_t_sm()
78 .w_full()
79 .gap_1p5()
80 .child(Icon::new(IconName::GitBranch).size(IconSize::XSmall))
81 .child(h_flex().gap_1().overflow_x_hidden().child(
82 div().max_w_96().overflow_x_hidden().text_ellipsis().child(
83 Headline::new(self.operation.clone()).size(HeadlineSize::XSmall),
84 ),
85 )),
86 )
87 .child(
88 div()
89 .text_buffer(cx)
90 .py_2()
91 .px_3()
92 .bg(cx.theme().colors().editor_background)
93 .border_t_1()
94 .border_color(cx.theme().colors().border_variant)
95 .size_full()
96 .overflow_hidden()
97 .child(self.prompt.clone())
98 .child(self.editor.clone()),
99 )
100 }
101}