welcome.rs

  1mod base_keymap_picker;
  2mod base_keymap_setting;
  3mod multibuffer_hint;
  4
  5use client::{telemetry::Telemetry, TelemetrySettings};
  6use db::kvp::KEY_VALUE_STORE;
  7use gpui::{
  8    actions, svg, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
  9    ParentElement, Render, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
 10    WindowContext,
 11};
 12use settings::{Settings, SettingsStore};
 13use std::sync::Arc;
 14use ui::{prelude::*, CheckboxWithLabel};
 15use vim::VimModeSetting;
 16use workspace::{
 17    dock::DockPosition,
 18    item::{Item, ItemEvent},
 19    open_new, AppState, Welcome, Workspace, WorkspaceId,
 20};
 21
 22pub use base_keymap_setting::BaseKeymap;
 23pub use multibuffer_hint::*;
 24
 25actions!(welcome, [ResetHints]);
 26
 27pub const FIRST_OPEN: &str = "first_open";
 28
 29pub fn init(cx: &mut AppContext) {
 30    BaseKeymap::register(cx);
 31
 32    cx.observe_new_views(|workspace: &mut Workspace, _cx| {
 33        workspace.register_action(|workspace, _: &Welcome, cx| {
 34            let welcome_page = WelcomePage::new(workspace, cx);
 35            workspace.add_item_to_active_pane(Box::new(welcome_page), None, true, cx)
 36        });
 37        workspace
 38            .register_action(|_workspace, _: &ResetHints, cx| MultibufferHint::set_count(0, cx));
 39    })
 40    .detach();
 41
 42    base_keymap_picker::init(cx);
 43}
 44
 45pub fn show_welcome_view(
 46    app_state: Arc<AppState>,
 47    cx: &mut AppContext,
 48) -> Task<anyhow::Result<()>> {
 49    open_new(app_state, cx, |workspace, cx| {
 50        workspace.toggle_dock(DockPosition::Left, cx);
 51        let welcome_page = WelcomePage::new(workspace, cx);
 52        workspace.add_item_to_center(Box::new(welcome_page.clone()), cx);
 53        cx.focus_view(&welcome_page);
 54        cx.notify();
 55
 56        db::write_and_log(cx, || {
 57            KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string())
 58        });
 59    })
 60}
 61
 62pub struct WelcomePage {
 63    workspace: WeakView<Workspace>,
 64    focus_handle: FocusHandle,
 65    telemetry: Arc<Telemetry>,
 66    _settings_subscription: Subscription,
 67}
 68
 69impl Render for WelcomePage {
 70    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
 71        h_flex()
 72            .size_full()
 73            .bg(cx.theme().colors().editor_background)
 74            .track_focus(&self.focus_handle)
 75            .child(
 76                v_flex()
 77                    .w_96()
 78                    .gap_4()
 79                    .mx_auto()
 80                    .child(
 81                        svg()
 82                            .path("icons/logo_96.svg")
 83                            .text_color(gpui::white())
 84                            .w(px(96.))
 85                            .h(px(96.))
 86                            .mx_auto(),
 87                    )
 88                    .child(
 89                        h_flex()
 90                            .justify_center()
 91                            .child(Label::new("Code at the speed of thought")),
 92                    )
 93                    .child(
 94                        v_flex()
 95                            .gap_2()
 96                            .child(
 97                                Button::new("choose-theme", "Choose a theme")
 98                                    .full_width()
 99                                    .on_click(cx.listener(|this, _, cx| {
100                                        this.telemetry.report_app_event(
101                                            "welcome page: change theme".to_string(),
102                                        );
103                                        this.workspace
104                                            .update(cx, |workspace, cx| {
105                                                theme_selector::toggle(
106                                                    workspace,
107                                                    &Default::default(),
108                                                    cx,
109                                                )
110                                            })
111                                            .ok();
112                                    })),
113                            )
114                            .child(
115                                Button::new("choose-keymap", "Choose a keymap")
116                                    .full_width()
117                                    .on_click(cx.listener(|this, _, cx| {
118                                        this.telemetry.report_app_event(
119                                            "welcome page: change keymap".to_string(),
120                                        );
121                                        this.workspace
122                                            .update(cx, |workspace, cx| {
123                                                base_keymap_picker::toggle(
124                                                    workspace,
125                                                    &Default::default(),
126                                                    cx,
127                                                )
128                                            })
129                                            .ok();
130                                    })),
131                            )
132                            .when(cfg!(target_os = "macos"), |el| {
133                                el.child(
134                                    Button::new("install-cli", "Install the CLI")
135                                        .full_width()
136                                        .on_click(cx.listener(|this, _, cx| {
137                                            this.telemetry.report_app_event(
138                                                "welcome page: install cli".to_string(),
139                                            );
140                                            cx.app_mut()
141                                                .spawn(|cx| async move {
142                                                    install_cli::install_cli(&cx).await
143                                                })
144                                                .detach_and_log_err(cx);
145                                        })),
146                                )
147                            })
148                            .child(
149                                Button::new("sign-in-to-copilot", "Sign in to GitHub Copilot")
150                                    .full_width()
151                                    .on_click(cx.listener(|this, _, cx| {
152                                        this.telemetry.report_app_event(
153                                            "welcome page: sign in to copilot".to_string(),
154                                        );
155                                        inline_completion_button::initiate_sign_in(cx);
156                                    })),
157                            )
158                            .child(
159                                Button::new("explore extensions", "Explore extensions")
160                                    .full_width()
161                                    .on_click(cx.listener(|this, _, cx| {
162                                        this.telemetry.report_app_event(
163                                            "welcome page: open extensions".to_string(),
164                                        );
165                                        cx.dispatch_action(Box::new(extensions_ui::Extensions));
166                                    })),
167                            ),
168                    )
169                    .child(
170                        v_flex()
171                            .p_3()
172                            .gap_2()
173                            .bg(cx.theme().colors().elevated_surface_background)
174                            .border_1()
175                            .border_color(cx.theme().colors().border)
176                            .rounded_md()
177                            .child(CheckboxWithLabel::new(
178                                "enable-vim",
179                                Label::new("Enable vim mode"),
180                                if VimModeSetting::get_global(cx).0 {
181                                    ui::Selection::Selected
182                                } else {
183                                    ui::Selection::Unselected
184                                },
185                                cx.listener(move |this, selection, cx| {
186                                    this.telemetry
187                                        .report_app_event("welcome page: toggle vim".to_string());
188                                    this.update_settings::<VimModeSetting>(
189                                        selection,
190                                        cx,
191                                        |setting, value| *setting = Some(value),
192                                    );
193                                }),
194                            ))
195                            .child(CheckboxWithLabel::new(
196                                "enable-telemetry",
197                                Label::new("Send anonymous usage data"),
198                                if TelemetrySettings::get_global(cx).metrics {
199                                    ui::Selection::Selected
200                                } else {
201                                    ui::Selection::Unselected
202                                },
203                                cx.listener(move |this, selection, cx| {
204                                    this.telemetry.report_app_event(
205                                        "welcome page: toggle metric telemetry".to_string(),
206                                    );
207                                    this.update_settings::<TelemetrySettings>(selection, cx, {
208                                        let telemetry = this.telemetry.clone();
209
210                                        move |settings, value| {
211                                            settings.metrics = Some(value);
212
213                                            telemetry.report_setting_event(
214                                                "metric telemetry",
215                                                value.to_string(),
216                                            );
217                                        }
218                                    });
219                                }),
220                            ))
221                            .child(CheckboxWithLabel::new(
222                                "enable-crash",
223                                Label::new("Send crash reports"),
224                                if TelemetrySettings::get_global(cx).diagnostics {
225                                    ui::Selection::Selected
226                                } else {
227                                    ui::Selection::Unselected
228                                },
229                                cx.listener(move |this, selection, cx| {
230                                    this.telemetry.report_app_event(
231                                        "welcome page: toggle diagnostic telemetry".to_string(),
232                                    );
233                                    this.update_settings::<TelemetrySettings>(selection, cx, {
234                                        let telemetry = this.telemetry.clone();
235
236                                        move |settings, value| {
237                                            settings.diagnostics = Some(value);
238
239                                            telemetry.report_setting_event(
240                                                "diagnostic telemetry",
241                                                value.to_string(),
242                                            );
243                                        }
244                                    });
245                                }),
246                            )),
247                    ),
248            )
249    }
250}
251
252impl WelcomePage {
253    pub fn new(workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
254        let this = cx.new_view(|cx| {
255            cx.on_release(|this: &mut Self, _, _| {
256                this.telemetry
257                    .report_app_event("welcome page: close".to_string());
258            })
259            .detach();
260
261            WelcomePage {
262                focus_handle: cx.focus_handle(),
263                workspace: workspace.weak_handle(),
264                telemetry: workspace.client().telemetry().clone(),
265                _settings_subscription: cx
266                    .observe_global::<SettingsStore>(move |_, cx| cx.notify()),
267            }
268        });
269
270        this
271    }
272
273    fn update_settings<T: Settings>(
274        &mut self,
275        selection: &Selection,
276        cx: &mut ViewContext<Self>,
277        callback: impl 'static + Send + Fn(&mut T::FileContent, bool),
278    ) {
279        if let Some(workspace) = self.workspace.upgrade() {
280            let fs = workspace.read(cx).app_state().fs.clone();
281            let selection = *selection;
282            settings::update_settings_file::<T>(fs, cx, move |settings, _| {
283                let value = match selection {
284                    Selection::Unselected => false,
285                    Selection::Selected => true,
286                    _ => return,
287                };
288
289                callback(settings, value)
290            });
291        }
292    }
293}
294
295impl EventEmitter<ItemEvent> for WelcomePage {}
296
297impl FocusableView for WelcomePage {
298    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
299        self.focus_handle.clone()
300    }
301}
302
303impl Item for WelcomePage {
304    type Event = ItemEvent;
305
306    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
307        Some("Welcome to Zed!".into())
308    }
309
310    fn telemetry_event_text(&self) -> Option<&'static str> {
311        Some("welcome page")
312    }
313
314    fn show_toolbar(&self) -> bool {
315        false
316    }
317
318    fn clone_on_split(
319        &self,
320        _workspace_id: Option<WorkspaceId>,
321        cx: &mut ViewContext<Self>,
322    ) -> Option<View<Self>> {
323        Some(cx.new_view(|cx| WelcomePage {
324            focus_handle: cx.focus_handle(),
325            workspace: self.workspace.clone(),
326            telemetry: self.telemetry.clone(),
327            _settings_subscription: cx.observe_global::<SettingsStore>(move |_, cx| cx.notify()),
328        }))
329    }
330
331    fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
332        f(*event)
333    }
334}