welcome.rs

  1mod base_keymap_picker;
  2
  3use std::{borrow::Cow, sync::Arc};
  4
  5use db::kvp::KEY_VALUE_STORE;
  6use gpui::{
  7    elements::{Flex, Label, MouseEventHandler, ParentElement},
  8    Action, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext,
  9    Subscription, View, ViewContext,
 10};
 11use settings::{settings_file::SettingsFile, Settings};
 12
 13use workspace::{
 14    item::Item, open_new, sidebar::SidebarSide, AppState, PaneBackdrop, Welcome, Workspace,
 15    WorkspaceId,
 16};
 17
 18use crate::base_keymap_picker::ToggleBaseKeymapSelector;
 19
 20pub const FIRST_OPEN: &str = "first_open";
 21
 22pub fn init(cx: &mut MutableAppContext) {
 23    cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| {
 24        let welcome_page = cx.add_view(WelcomePage::new);
 25        workspace.add_item(Box::new(welcome_page), cx)
 26    });
 27
 28    base_keymap_picker::init(cx);
 29}
 30
 31pub fn show_welcome_experience(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
 32    open_new(&app_state, cx, |workspace, cx| {
 33        workspace.toggle_sidebar(SidebarSide::Left, cx);
 34        let welcome_page = cx.add_view(|cx| WelcomePage::new(cx));
 35        workspace.add_item_to_center(Box::new(welcome_page.clone()), cx);
 36        cx.focus(welcome_page);
 37        cx.notify();
 38    })
 39    .detach();
 40
 41    db::write_and_log(cx, || {
 42        KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string())
 43    });
 44}
 45
 46pub struct WelcomePage {
 47    _settings_subscription: Subscription,
 48}
 49
 50impl Entity for WelcomePage {
 51    type Event = ();
 52}
 53
 54impl View for WelcomePage {
 55    fn ui_name() -> &'static str {
 56        "WelcomePage"
 57    }
 58
 59    fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
 60        let self_handle = cx.handle();
 61        let settings = cx.global::<Settings>();
 62        let theme = settings.theme.clone();
 63
 64        let width = theme.welcome.page_width;
 65
 66        let (diagnostics, metrics) = {
 67            let telemetry = settings.telemetry();
 68            (telemetry.diagnostics(), telemetry.metrics())
 69        };
 70
 71        enum Metrics {}
 72        enum Diagnostics {}
 73
 74        PaneBackdrop::new(
 75            self_handle.id(),
 76            Flex::column()
 77                .with_children([
 78                    Flex::column()
 79                        .with_children([
 80                            theme::ui::icon(&theme.welcome.logo)
 81                                .aligned()
 82                                .contained()
 83                                .aligned()
 84                                .boxed(),
 85                            Label::new(
 86                                "Code at the speed of thought",
 87                                theme.welcome.logo_subheading.text.clone(),
 88                            )
 89                            .aligned()
 90                            .contained()
 91                            .with_style(theme.welcome.logo_subheading.container)
 92                            .boxed(),
 93                        ])
 94                        .contained()
 95                        .with_style(theme.welcome.heading_group)
 96                        .constrained()
 97                        .with_width(width)
 98                        .boxed(),
 99                    Flex::column()
100                        .with_children([
101                            self.render_cta_button(
102                                "Choose a theme",
103                                theme_selector::Toggle,
104                                width,
105                                cx,
106                            ),
107                            self.render_cta_button(
108                                "Choose a keymap",
109                                ToggleBaseKeymapSelector,
110                                width,
111                                cx,
112                            ),
113                            self.render_cta_button(
114                                "Install the CLI",
115                                install_cli::Install,
116                                width,
117                                cx,
118                            ),
119                        ])
120                        .contained()
121                        .with_style(theme.welcome.button_group)
122                        .constrained()
123                        .with_width(width)
124                        .boxed(),
125                    Flex::column()
126                        .with_children([
127                            theme::ui::checkbox_with_label::<Metrics, Self>(
128                                Flex::column()
129                                    .with_children([
130                                        Label::new(
131                                            "Send anonymous usage data",
132                                            theme.welcome.checkbox.label.text.clone(),
133                                        )
134                                        .contained()
135                                        .with_style(theme.welcome.checkbox.label.container)
136                                        .boxed(),
137                                        Label::new(
138                                            "Help > View Telemetry",
139                                            theme.welcome.usage_note.text.clone(),
140                                        )
141                                        .contained()
142                                        .with_style(theme.welcome.usage_note.container)
143                                        .boxed(),
144                                    ])
145                                    .boxed(),
146                                &theme.welcome.checkbox,
147                                metrics,
148                                cx,
149                                |checked, cx| {
150                                    SettingsFile::update(cx, move |file| {
151                                        file.telemetry.set_metrics(checked)
152                                    })
153                                },
154                            )
155                            .contained()
156                            .with_style(theme.welcome.checkbox_container)
157                            .boxed(),
158                            theme::ui::checkbox::<Diagnostics, Self>(
159                                "Send crash reports",
160                                &theme.welcome.checkbox,
161                                diagnostics,
162                                cx,
163                                |checked, cx| {
164                                    SettingsFile::update(cx, move |file| {
165                                        file.telemetry.set_diagnostics(checked)
166                                    })
167                                },
168                            )
169                            .contained()
170                            .with_style(theme.welcome.checkbox_container)
171                            .boxed(),
172                        ])
173                        .contained()
174                        .with_style(theme.welcome.checkbox_group)
175                        .constrained()
176                        .with_width(width)
177                        .boxed(),
178                ])
179                .constrained()
180                .with_max_width(width)
181                .contained()
182                .with_uniform_padding(10.)
183                .aligned()
184                .boxed(),
185        )
186        .boxed()
187    }
188}
189
190impl WelcomePage {
191    pub fn new(cx: &mut ViewContext<Self>) -> Self {
192        let handle = cx.weak_handle();
193
194        let settings_subscription = cx.observe_global::<Settings, _>(move |cx| {
195            if let Some(handle) = handle.upgrade(cx) {
196                handle.update(cx, |_, cx| cx.notify())
197            }
198        });
199
200        WelcomePage {
201            _settings_subscription: settings_subscription,
202        }
203    }
204
205    fn render_cta_button<L, A>(
206        &self,
207        label: L,
208        action: A,
209        width: f32,
210        cx: &mut RenderContext<Self>,
211    ) -> ElementBox
212    where
213        L: Into<Cow<'static, str>>,
214        A: 'static + Action + Clone,
215    {
216        let theme = cx.global::<Settings>().theme.clone();
217        MouseEventHandler::<A>::new(0, cx, |state, _| {
218            let style = theme.welcome.button.style_for(state, false);
219            Label::new(label, style.text.clone())
220                .aligned()
221                .contained()
222                .with_style(style.container)
223                .constrained()
224                .with_max_width(width)
225                .boxed()
226        })
227        .on_click(MouseButton::Left, move |_, cx| {
228            cx.dispatch_action(action.clone())
229        })
230        .with_cursor_style(gpui::CursorStyle::PointingHand)
231        .boxed()
232    }
233
234    // fn render_settings_checkbox<T: 'static>(
235    //     &self,
236    //     label: &'static str,
237    //     style: &CheckboxStyle,
238    //     checked: bool,
239    //     cx: &mut RenderContext<Self>,
240    //     set_value: fn(&mut SettingsFileContent, checked: bool) -> (),
241    // ) -> ElementBox {
242    //     MouseEventHandler::<T>::new(0, cx, |state, _| {
243    //         let indicator = if checked {
244    //             Svg::new(style.check_icon.clone())
245    //                 .with_color(style.check_icon_color)
246    //                 .constrained()
247    //         } else {
248    //             Empty::new().constrained()
249    //         };
250
251    //         Flex::row()
252    //             .with_children([
253    //                 indicator
254    //                     .with_width(style.width)
255    //                     .with_height(style.height)
256    //                     .contained()
257    //                     .with_style(if checked {
258    //                         if state.hovered() {
259    //                             style.hovered_and_checked
260    //                         } else {
261    //                             style.checked
262    //                         }
263    //                     } else {
264    //                         if state.hovered() {
265    //                             style.hovered
266    //                         } else {
267    //                             style.default
268    //                         }
269    //                     })
270    //                     .boxed(),
271    //                 Label::new(label, style.label.text.clone())
272    //                     .contained()
273    //                     .with_style(style.label.container)
274    //                     .boxed(),
275    //             ])
276    //             .align_children_center()
277    //             .boxed()
278    //     })
279    //     .on_click(gpui::MouseButton::Left, move |_, cx| {
280    //         SettingsFile::update(cx, move |content| set_value(content, !checked))
281    //     })
282    //     .with_cursor_style(gpui::CursorStyle::PointingHand)
283    //     .contained()
284    //     .with_style(style.container)
285    //     .boxed()
286    // }
287}
288
289impl Item for WelcomePage {
290    fn tab_content(
291        &self,
292        _detail: Option<usize>,
293        style: &theme::Tab,
294        _cx: &gpui::AppContext,
295    ) -> gpui::ElementBox {
296        Flex::row()
297            .with_child(
298                Label::new("Welcome to Zed!", style.label.clone())
299                    .aligned()
300                    .contained()
301                    .boxed(),
302            )
303            .boxed()
304    }
305
306    fn show_toolbar(&self) -> bool {
307        false
308    }
309    fn clone_on_split(
310        &self,
311        _workspace_id: WorkspaceId,
312        cx: &mut ViewContext<Self>,
313    ) -> Option<Self> {
314        Some(WelcomePage::new(cx))
315    }
316}