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