askpass_modal.rs

  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}