1mod base_keymap_picker;
2mod base_keymap_setting;
3
4use client::TelemetrySettings;
5use db::kvp::KEY_VALUE_STORE;
6use gpui::{
7 svg, AnyElement, AppContext, Div, EventEmitter, FocusHandle, Focusable, FocusableView,
8 InteractiveElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
9 VisualContext, WeakView, WindowContext,
10};
11use settings::{Settings, SettingsStore};
12use std::sync::Arc;
13use ui::{prelude::*, Checkbox};
14use vim::VimModeSetting;
15use workspace::{
16 dock::DockPosition,
17 item::{Item, ItemEvent},
18 open_new, AppState, Welcome, Workspace, WorkspaceId,
19};
20
21pub use base_keymap_setting::BaseKeymap;
22
23pub const FIRST_OPEN: &str = "first_open";
24
25pub fn init(cx: &mut AppContext) {
26 BaseKeymap::register(cx);
27
28 cx.observe_new_views(|workspace: &mut Workspace, _cx| {
29 workspace.register_action(|workspace, _: &Welcome, cx| {
30 let welcome_page = cx.build_view(|cx| WelcomePage::new(workspace, cx));
31 workspace.add_item(Box::new(welcome_page), cx)
32 });
33 })
34 .detach();
35
36 base_keymap_picker::init(cx);
37}
38
39pub fn show_welcome_view(app_state: &Arc<AppState>, cx: &mut AppContext) {
40 open_new(&app_state, cx, |workspace, cx| {
41 workspace.toggle_dock(DockPosition::Left, cx);
42 let welcome_page = cx.build_view(|cx| WelcomePage::new(workspace, cx));
43 workspace.add_item_to_center(Box::new(welcome_page.clone()), cx);
44 cx.focus_view(&welcome_page);
45 cx.notify();
46 })
47 .detach();
48
49 db::write_and_log(cx, || {
50 KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string())
51 });
52}
53
54pub struct WelcomePage {
55 workspace: WeakView<Workspace>,
56 focus_handle: FocusHandle,
57 _settings_subscription: Subscription,
58}
59
60impl Render for WelcomePage {
61 type Element = Focusable<Div>;
62
63 fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
64 h_stack().full().track_focus(&self.focus_handle).child(
65 v_stack()
66 .w_96()
67 .gap_4()
68 .mx_auto()
69 .child(
70 svg()
71 .path("icons/logo_96.svg")
72 .text_color(gpui::white())
73 .w(px(96.))
74 .h(px(96.))
75 .mx_auto(),
76 )
77 .child(Label::new("Code at the speed of thought"))
78 .child(
79 v_stack()
80 .gap_2()
81 .child(
82 Button::new("choose-theme", "Choose a theme")
83 .full_width()
84 .on_click(cx.listener(|this, _, cx| {
85 this.workspace
86 .update(cx, |workspace, cx| {
87 theme_selector::toggle(
88 workspace,
89 &Default::default(),
90 cx,
91 )
92 })
93 .ok();
94 })),
95 )
96 .child(
97 Button::new("choose-keymap", "Choose a keymap")
98 .full_width()
99 .on_click(cx.listener(|this, _, cx| {
100 this.workspace
101 .update(cx, |workspace, cx| {
102 base_keymap_picker::toggle(
103 workspace,
104 &Default::default(),
105 cx,
106 )
107 })
108 .ok();
109 })),
110 )
111 .child(
112 Button::new("install-cli", "Install the CLI")
113 .full_width()
114 .on_click(cx.listener(|_, _, cx| {
115 cx.app_mut()
116 .spawn(
117 |cx| async move { install_cli::install_cli(&cx).await },
118 )
119 .detach_and_log_err(cx);
120 })),
121 ),
122 )
123 .child(
124 v_stack()
125 .p_3()
126 .gap_2()
127 .bg(cx.theme().colors().elevated_surface_background)
128 .border_1()
129 .border_color(cx.theme().colors().border)
130 .rounded_md()
131 .child(
132 h_stack()
133 .gap_2()
134 .child(
135 Checkbox::new(
136 "enable-vim",
137 if VimModeSetting::get_global(cx).0 {
138 ui::Selection::Selected
139 } else {
140 ui::Selection::Unselected
141 },
142 )
143 .on_click(cx.listener(
144 move |this, selection, cx| {
145 this.update_settings::<VimModeSetting>(
146 selection,
147 cx,
148 |setting, value| *setting = Some(value),
149 );
150 },
151 )),
152 )
153 .child(Label::new("Enable vim mode")),
154 )
155 .child(
156 h_stack()
157 .gap_2()
158 .child(
159 Checkbox::new(
160 "enable-telemetry",
161 if TelemetrySettings::get_global(cx).metrics {
162 ui::Selection::Selected
163 } else {
164 ui::Selection::Unselected
165 },
166 )
167 .on_click(cx.listener(
168 move |this, selection, cx| {
169 this.update_settings::<TelemetrySettings>(
170 selection,
171 cx,
172 |settings, value| settings.metrics = Some(value),
173 );
174 },
175 )),
176 )
177 .child(Label::new("Send anonymous usage data")),
178 )
179 .child(
180 h_stack()
181 .gap_2()
182 .child(
183 Checkbox::new(
184 "enable-crash",
185 if TelemetrySettings::get_global(cx).diagnostics {
186 ui::Selection::Selected
187 } else {
188 ui::Selection::Unselected
189 },
190 )
191 .on_click(cx.listener(
192 move |this, selection, cx| {
193 this.update_settings::<TelemetrySettings>(
194 selection,
195 cx,
196 |settings, value| {
197 settings.diagnostics = Some(value)
198 },
199 );
200 },
201 )),
202 )
203 .child(Label::new("Send crash reports")),
204 ),
205 ),
206 )
207 }
208}
209
210impl WelcomePage {
211 pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
212 WelcomePage {
213 focus_handle: cx.focus_handle(),
214 workspace: workspace.weak_handle(),
215 _settings_subscription: cx.observe_global::<SettingsStore>(move |_, cx| cx.notify()),
216 }
217 }
218
219 fn update_settings<T: Settings>(
220 &mut self,
221 selection: &Selection,
222 cx: &mut ViewContext<Self>,
223 callback: impl 'static + Send + Fn(&mut T::FileContent, bool),
224 ) {
225 if let Some(workspace) = self.workspace.upgrade() {
226 let fs = workspace.read(cx).app_state().fs.clone();
227 let selection = *selection;
228 settings::update_settings_file::<T>(fs, cx, move |settings| {
229 let value = match selection {
230 Selection::Unselected => false,
231 Selection::Selected => true,
232 _ => return,
233 };
234
235 callback(settings, value)
236 });
237 }
238 }
239}
240
241impl EventEmitter<ItemEvent> for WelcomePage {}
242
243impl FocusableView for WelcomePage {
244 fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
245 self.focus_handle.clone()
246 }
247}
248
249impl Item for WelcomePage {
250 type Event = ItemEvent;
251
252 fn tab_content(&self, _: Option<usize>, selected: bool, _: &WindowContext) -> AnyElement {
253 Label::new("Welcome to Zed!")
254 .color(if selected {
255 Color::Default
256 } else {
257 Color::Muted
258 })
259 .into_any_element()
260 }
261
262 fn show_toolbar(&self) -> bool {
263 false
264 }
265
266 fn clone_on_split(
267 &self,
268 _workspace_id: WorkspaceId,
269 cx: &mut ViewContext<Self>,
270 ) -> Option<View<Self>> {
271 Some(cx.build_view(|cx| WelcomePage {
272 focus_handle: cx.focus_handle(),
273 workspace: self.workspace.clone(),
274 _settings_subscription: cx.observe_global::<SettingsStore>(move |_, cx| cx.notify()),
275 }))
276 }
277
278 fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
279 f(*event)
280 }
281}