From 663bbb06d9e80d4f61ea5f2ae3598e6c930d04dd Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 21 Nov 2023 12:40:00 -0800 Subject: [PATCH 1/3] WIP --- Cargo.lock | 46 +++ Cargo.toml | 2 + crates/client2/src/client2.rs | 8 +- crates/gpui2/src/action.rs | 1 + crates/theme2/src/registry.rs | 4 + crates/theme_selector2/Cargo.toml | 28 ++ crates/theme_selector2/src/theme_selector.rs | 254 ++++++++++++++++ crates/welcome2/Cargo.toml | 36 +++ crates/welcome2/src/base_keymap_picker.rs | 152 ++++++++++ crates/welcome2/src/base_keymap_setting.rs | 65 +++++ crates/welcome2/src/welcome.rs | 287 +++++++++++++++++++ crates/zed2/Cargo.toml | 4 +- crates/zed2/src/main.rs | 83 +++--- script/crate-dep-graph | 2 +- 14 files changed, 921 insertions(+), 51 deletions(-) create mode 100644 crates/theme_selector2/Cargo.toml create mode 100644 crates/theme_selector2/src/theme_selector.rs create mode 100644 crates/welcome2/Cargo.toml create mode 100644 crates/welcome2/src/base_keymap_picker.rs create mode 100644 crates/welcome2/src/base_keymap_setting.rs create mode 100644 crates/welcome2/src/welcome.rs diff --git a/Cargo.lock b/Cargo.lock index 6aa94b08d05cd..2184ba5ebb4c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9405,6 +9405,26 @@ dependencies = [ "workspace", ] +[[package]] +name = "theme_selector2" +version = "0.1.0" +dependencies = [ + "editor2", + "feature_flags2", + "fs2", + "fuzzy2", + "gpui2", + "log", + "parking_lot 0.11.2", + "picker2", + "postage", + "settings2", + "smol", + "theme2", + "util", + "workspace2", +] + [[package]] name = "thiserror" version = "1.0.48" @@ -10978,6 +10998,30 @@ dependencies = [ "workspace", ] +[[package]] +name = "welcome2" +version = "0.1.0" +dependencies = [ + "anyhow", + "client2", + "db2", + "editor2", + "fs2", + "fuzzy2", + "gpui2", + "install_cli2", + "log", + "picker2", + "project2", + "schemars", + "serde", + "settings2", + "theme2", + "theme_selector2", + "util", + "workspace2", +] + [[package]] name = "which" version = "4.4.2" @@ -11640,6 +11684,7 @@ dependencies = [ "terminal_view2", "text2", "theme2", + "theme_selector2", "thiserror", "tiny_http", "toml 0.5.11", @@ -11676,6 +11721,7 @@ dependencies = [ "urlencoding", "util", "uuid 1.4.1", + "welcome2", "workspace2", "zed_actions2", ] diff --git a/Cargo.toml b/Cargo.toml index d7b9918f624a5..6c1152cf9c359 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,6 +108,7 @@ members = [ "crates/theme2", "crates/theme_importer", "crates/theme_selector", + "crates/theme_selector2", "crates/ui2", "crates/util", "crates/semantic_index", @@ -115,6 +116,7 @@ members = [ "crates/vcs_menu", "crates/workspace2", "crates/welcome", + "crates/welcome2", "crates/xtask", "crates/zed", "crates/zed2", diff --git a/crates/client2/src/client2.rs b/crates/client2/src/client2.rs index b4279b023ecd7..028dec6803069 100644 --- a/crates/client2/src/client2.rs +++ b/crates/client2/src/client2.rs @@ -694,8 +694,8 @@ impl Client { } } - pub async fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool { - read_credentials_from_keychain(cx).await.is_some() + pub fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool { + read_credentials_from_keychain(cx).is_some() } #[async_recursion(?Send)] @@ -726,7 +726,7 @@ impl Client { let mut read_from_keychain = false; let mut credentials = self.state.read().credentials.clone(); if credentials.is_none() && try_keychain { - credentials = read_credentials_from_keychain(cx).await; + credentials = read_credentials_from_keychain(cx); read_from_keychain = credentials.is_some(); } if credentials.is_none() { @@ -1325,7 +1325,7 @@ impl Client { } } -async fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option { +fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option { if IMPERSONATE_LOGIN.is_some() { return None; } diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index 958eaabdb8307..03ef2d2281876 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -162,6 +162,7 @@ macro_rules! actions { ( $name:ident ) => { #[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, gpui::serde_derive::Deserialize, gpui::Action)] + #[serde(crate = "gpui::serde")] pub struct $name; }; diff --git a/crates/theme2/src/registry.rs b/crates/theme2/src/registry.rs index 919dd1b1099ec..b50eb831dda51 100644 --- a/crates/theme2/src/registry.rs +++ b/crates/theme2/src/registry.rs @@ -86,6 +86,10 @@ impl ThemeRegistry { })); } + pub fn clear(&mut self) { + self.themes.clear(); + } + pub fn list_names(&self, _staff: bool) -> impl Iterator + '_ { self.themes.keys().cloned() } diff --git a/crates/theme_selector2/Cargo.toml b/crates/theme_selector2/Cargo.toml new file mode 100644 index 0000000000000..89b7487a7bd45 --- /dev/null +++ b/crates/theme_selector2/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "theme_selector2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/theme_selector.rs" +doctest = false + +[dependencies] +editor = { package = "editor2", path = "../editor2" } +fuzzy = { package = "fuzzy2", path = "../fuzzy2" } +fs = { package = "fs2", path = "../fs2" } +gpui = { package = "gpui2", path = "../gpui2" } +picker = { package = "picker2", path = "../picker2" } +theme = { package = "theme2", path = "../theme2" } +settings = { package = "settings2", path = "../settings2" } +feature_flags = { package = "feature_flags2", path = "../feature_flags2" } +workspace = { package = "workspace2", path = "../workspace2" } +util = { path = "../util" } +log.workspace = true +parking_lot.workspace = true +postage.workspace = true +smol.workspace = true + +[dev-dependencies] +editor = { package = "editor2", path = "../editor2", features = ["test-support"] } diff --git a/crates/theme_selector2/src/theme_selector.rs b/crates/theme_selector2/src/theme_selector.rs new file mode 100644 index 0000000000000..6e660caf51bf4 --- /dev/null +++ b/crates/theme_selector2/src/theme_selector.rs @@ -0,0 +1,254 @@ +use feature_flags::FeatureFlagAppExt; +use fs::Fs; +use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; +use gpui::{ + actions, div, AppContext, Div, EventEmitter, FocusableView, Manager, Render, SharedString, + View, ViewContext, VisualContext, +}; +use picker::{Picker, PickerDelegate}; +use settings::{update_settings_file, SettingsStore}; +use std::sync::Arc; +use theme::{ActiveTheme, Theme, ThemeRegistry, ThemeSettings}; +use util::ResultExt; +use workspace::{ui::HighlightedLabel, Workspace}; + +actions!(Toggle, Reload); + +pub fn init(cx: &mut AppContext) { + cx.observe_new_views( + |workspace: &mut Workspace, cx: &mut ViewContext| { + workspace.register_action(toggle); + }, + ); +} + +pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { + let fs = workspace.app_state().fs.clone(); + workspace.toggle_modal(cx, |cx| { + ThemeSelector::new(ThemeSelectorDelegate::new(fs, cx), cx) + }); +} + +#[cfg(debug_assertions)] +pub fn reload(cx: &mut AppContext) { + let current_theme_name = cx.theme().name.clone(); + let registry = cx.global::>(); + registry.clear(); + match registry.get(¤t_theme_name) { + Ok(theme) => { + ThemeSelectorDelegate::set_theme(theme, cx); + log::info!("reloaded theme {}", current_theme_name); + } + Err(error) => { + log::error!("failed to load theme {}: {:?}", current_theme_name, error) + } + } +} + +pub struct ThemeSelector { + picker: View>, +} + +impl EventEmitter for ThemeSelector {} + +impl FocusableView for ThemeSelector { + fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { + self.picker.focus_handle(cx) + } +} + +impl Render for ThemeSelector { + type Element = View>; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + self.picker.clone() + } +} + +impl ThemeSelector { + pub fn new(delegate: ThemeSelectorDelegate, cx: &mut ViewContext) -> Self { + let picker = cx.build_view(|cx| Picker::new(delegate, cx)); + Self { picker } + } +} + +pub struct ThemeSelectorDelegate { + fs: Arc, + theme_names: Vec, + matches: Vec, + original_theme: Arc, + selection_completed: bool, + selected_index: usize, +} + +impl ThemeSelectorDelegate { + fn new(fs: Arc, cx: &mut ViewContext) -> Self { + let original_theme = cx.theme().clone(); + + let staff_mode = cx.is_staff(); + let registry = cx.global::>(); + let mut theme_names = registry.list(staff_mode).collect::>(); + theme_names.sort_unstable_by(|a, b| a.is_light.cmp(&b.is_light).then(a.name.cmp(&b.name))); + let matches = theme_names + .iter() + .map(|meta| StringMatch { + candidate_id: 0, + score: 0.0, + positions: Default::default(), + string: meta.to_string(), + }) + .collect(); + let mut this = Self { + fs, + theme_names, + matches, + original_theme: original_theme.clone(), + selected_index: 0, + selection_completed: false, + }; + this.select_if_matching(&original_theme.meta.name); + this + } + + fn show_selected_theme(&mut self, cx: &mut ViewContext) { + if let Some(mat) = self.matches.get(self.selected_index) { + let registry = cx.global::>(); + match registry.get(&mat.string) { + Ok(theme) => { + Self::set_theme(theme, cx); + } + Err(error) => { + log::error!("error loading theme {}: {}", mat.string, error) + } + } + } + } + + fn select_if_matching(&mut self, theme_name: &str) { + self.selected_index = self + .matches + .iter() + .position(|mat| mat.string == theme_name) + .unwrap_or(self.selected_index); + } + + fn set_theme(theme: Arc, cx: &mut AppContext) { + cx.update_global::(|store, cx| { + let mut theme_settings = store.get::(None).clone(); + theme_settings.theme = theme; + store.override_global(theme_settings); + cx.refresh_windows(); + }); + } +} + +impl PickerDelegate for ThemeSelectorDelegate { + type ListItem = Div; + + fn placeholder_text(&self) -> Arc { + "Select Theme...".into() + } + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { + self.selection_completed = true; + + let theme_name = cx.theme().meta.name.clone(); + update_settings_file::(self.fs.clone(), cx, |settings| { + settings.theme = Some(theme_name); + }); + + cx.emit(Manager::Dismiss); + } + + fn dismissed(&mut self, cx: &mut ViewContext) { + if !self.selection_completed { + Self::set_theme(self.original_theme.clone(), cx); + self.selection_completed = true; + } + } + + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext) { + self.selected_index = ix; + self.show_selected_theme(cx); + } + + fn update_matches( + &mut self, + query: String, + cx: &mut ViewContext, + ) -> gpui::Task<()> { + let background = cx.background().clone(); + let candidates = self + .theme_names + .iter() + .enumerate() + .map(|(id, meta)| StringMatchCandidate { + id, + char_bag: meta.name.as_str().into(), + string: meta.name.clone(), + }) + .collect::>(); + + cx.spawn(|this, mut cx| async move { + let matches = if query.is_empty() { + candidates + .into_iter() + .enumerate() + .map(|(index, candidate)| StringMatch { + candidate_id: index, + string: candidate.string, + positions: Vec::new(), + score: 0.0, + }) + .collect() + } else { + match_strings( + &candidates, + &query, + false, + 100, + &Default::default(), + background, + ) + .await + }; + + this.update(&mut cx, |this, cx| { + let delegate = this.delegate_mut(); + delegate.matches = matches; + delegate.selected_index = delegate + .selected_index + .min(delegate.matches.len().saturating_sub(1)); + delegate.show_selected_theme(cx); + }) + .log_err(); + }) + } + + fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> Self::ListItem { + let theme = cx.theme(); + let colors = theme.colors(); + + let theme_match = &self.matches[ix]; + div() + .px_1() + .text_color(colors.text) + .text_ui() + .bg(colors.ghost_element_background) + .rounded_md() + .when(selected, |this| this.bg(colors.ghost_element_selected)) + .hover(|this| this.bg(colors.ghost_element_hover)) + .child(HighlightedLabel::new( + theme_match.string.clone(), + theme_match.positions.clone(), + )) + } +} diff --git a/crates/welcome2/Cargo.toml b/crates/welcome2/Cargo.toml new file mode 100644 index 0000000000000..0a2d2fd781530 --- /dev/null +++ b/crates/welcome2/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "welcome2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/welcome.rs" + +[features] +test-support = [] + +[dependencies] +client = { package = "client2", path = "../client2" } +editor = { package = "editor2", path = "../editor2" } +fs = { package = "fs2", path = "../fs2" } +fuzzy = { package = "fuzzy2", path = "../fuzzy2" } +gpui = { package = "gpui2", path = "../gpui2" } +db = { package = "db2", path = "../db2" } +install_cli = { package = "install_cli2", path = "../install_cli2" } +project = { package = "project2", path = "../project2" } +settings = { package = "settings2", path = "../settings2" } +theme = { package = "theme2", path = "../theme2" } +theme_selector = { package = "theme_selector2", path = "../theme_selector2" } +util = { path = "../util" } +picker = { package = "picker2", path = "../picker2" } +workspace = { package = "workspace2", path = "../workspace2" } +# vim = { package = "vim2", path = "../vim2" } + +anyhow.workspace = true +log.workspace = true +schemars.workspace = true +serde.workspace = true + +[dev-dependencies] +editor = { package = "editor2", path = "../editor2", features = ["test-support"] } diff --git a/crates/welcome2/src/base_keymap_picker.rs b/crates/welcome2/src/base_keymap_picker.rs new file mode 100644 index 0000000000000..021e3b86a0c7c --- /dev/null +++ b/crates/welcome2/src/base_keymap_picker.rs @@ -0,0 +1,152 @@ +use super::base_keymap_setting::BaseKeymap; +use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; +use gpui::{ + actions, + elements::{Element as _, Label}, + AppContext, Task, ViewContext, +}; +use picker::{Picker, PickerDelegate, PickerEvent}; +use project::Fs; +use settings::update_settings_file; +use std::sync::Arc; +use util::ResultExt; +use workspace::Workspace; + +actions!(welcome, [ToggleBaseKeymapSelector]); + +pub fn init(cx: &mut AppContext) { + cx.add_action(toggle); + BaseKeymapSelector::init(cx); +} + +pub fn toggle( + workspace: &mut Workspace, + _: &ToggleBaseKeymapSelector, + cx: &mut ViewContext, +) { + workspace.toggle_modal(cx, |workspace, cx| { + let fs = workspace.app_state().fs.clone(); + cx.add_view(|cx| BaseKeymapSelector::new(BaseKeymapSelectorDelegate::new(fs, cx), cx)) + }); +} + +pub type BaseKeymapSelector = Picker; + +pub struct BaseKeymapSelectorDelegate { + matches: Vec, + selected_index: usize, + fs: Arc, +} + +impl BaseKeymapSelectorDelegate { + fn new(fs: Arc, cx: &mut ViewContext) -> Self { + let base = settings::get::(cx); + let selected_index = BaseKeymap::OPTIONS + .iter() + .position(|(_, value)| value == base) + .unwrap_or(0); + Self { + matches: Vec::new(), + selected_index, + fs, + } + } +} + +impl PickerDelegate for BaseKeymapSelectorDelegate { + fn placeholder_text(&self) -> Arc { + "Select a base keymap...".into() + } + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext) { + self.selected_index = ix; + } + + fn update_matches( + &mut self, + query: String, + cx: &mut ViewContext, + ) -> Task<()> { + let background = cx.background().clone(); + let candidates = BaseKeymap::names() + .enumerate() + .map(|(id, name)| StringMatchCandidate { + id, + char_bag: name.into(), + string: name.into(), + }) + .collect::>(); + + cx.spawn(|this, mut cx| async move { + let matches = if query.is_empty() { + candidates + .into_iter() + .enumerate() + .map(|(index, candidate)| StringMatch { + candidate_id: index, + string: candidate.string, + positions: Vec::new(), + score: 0.0, + }) + .collect() + } else { + match_strings( + &candidates, + &query, + false, + 100, + &Default::default(), + background, + ) + .await + }; + + this.update(&mut cx, |this, _| { + let delegate = this.delegate_mut(); + delegate.matches = matches; + delegate.selected_index = delegate + .selected_index + .min(delegate.matches.len().saturating_sub(1)); + }) + .log_err(); + }) + } + + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { + if let Some(selection) = self.matches.get(self.selected_index) { + let base_keymap = BaseKeymap::from_names(&selection.string); + update_settings_file::(self.fs.clone(), cx, move |setting| { + *setting = Some(base_keymap) + }); + } + cx.emit(PickerEvent::Dismiss); + } + + fn dismissed(&mut self, _cx: &mut ViewContext) {} + + fn render_match( + &self, + ix: usize, + mouse_state: &mut gpui::MouseState, + selected: bool, + cx: &gpui::AppContext, + ) -> gpui::AnyElement> { + let theme = &theme::current(cx); + let keymap_match = &self.matches[ix]; + let style = theme.picker.item.in_state(selected).style_for(mouse_state); + + Label::new(keymap_match.string.clone(), style.label.clone()) + .with_highlights(keymap_match.positions.clone()) + .contained() + .with_style(style.container) + .into_any() + } +} diff --git a/crates/welcome2/src/base_keymap_setting.rs b/crates/welcome2/src/base_keymap_setting.rs new file mode 100644 index 0000000000000..c5b6171f9b4fb --- /dev/null +++ b/crates/welcome2/src/base_keymap_setting.rs @@ -0,0 +1,65 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use settings::Setting; + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] +pub enum BaseKeymap { + #[default] + VSCode, + JetBrains, + SublimeText, + Atom, + TextMate, +} + +impl BaseKeymap { + pub const OPTIONS: [(&'static str, Self); 5] = [ + ("VSCode (Default)", Self::VSCode), + ("Atom", Self::Atom), + ("JetBrains", Self::JetBrains), + ("Sublime Text", Self::SublimeText), + ("TextMate", Self::TextMate), + ]; + + pub fn asset_path(&self) -> Option<&'static str> { + match self { + BaseKeymap::JetBrains => Some("keymaps/jetbrains.json"), + BaseKeymap::SublimeText => Some("keymaps/sublime_text.json"), + BaseKeymap::Atom => Some("keymaps/atom.json"), + BaseKeymap::TextMate => Some("keymaps/textmate.json"), + BaseKeymap::VSCode => None, + } + } + + pub fn names() -> impl Iterator { + Self::OPTIONS.iter().map(|(name, _)| *name) + } + + pub fn from_names(option: &str) -> BaseKeymap { + Self::OPTIONS + .iter() + .copied() + .find_map(|(name, value)| (name == option).then(|| value)) + .unwrap_or_default() + } +} + +impl Setting for BaseKeymap { + const KEY: Option<&'static str> = Some("base_keymap"); + + type FileContent = Option; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result + where + Self: Sized, + { + Ok(user_values + .first() + .and_then(|v| **v) + .unwrap_or(default_value.unwrap())) + } +} diff --git a/crates/welcome2/src/welcome.rs b/crates/welcome2/src/welcome.rs new file mode 100644 index 0000000000000..a5d95429bdf00 --- /dev/null +++ b/crates/welcome2/src/welcome.rs @@ -0,0 +1,287 @@ +mod base_keymap_picker; +mod base_keymap_setting; + +use crate::base_keymap_picker::ToggleBaseKeymapSelector; +use client::TelemetrySettings; +use db::kvp::KEY_VALUE_STORE; +use gpui::{ + elements::{Flex, Label, ParentElement}, + AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, WeakViewHandle, +}; +use settings::{update_settings_file, SettingsStore}; +use std::{borrow::Cow, sync::Arc}; +use vim::VimModeSetting; +use workspace::{ + dock::DockPosition, item::Item, open_new, AppState, PaneBackdrop, Welcome, Workspace, + WorkspaceId, +}; + +pub use base_keymap_setting::BaseKeymap; + +pub const FIRST_OPEN: &str = "first_open"; + +pub fn init(cx: &mut AppContext) { + settings::register::(cx); + + cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { + let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); + workspace.add_item(Box::new(welcome_page), cx) + }); + + base_keymap_picker::init(cx); +} + +pub fn show_welcome_experience(app_state: &Arc, cx: &mut AppContext) { + open_new(&app_state, cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Left, cx); + let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); + workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); + cx.focus(&welcome_page); + cx.notify(); + }) + .detach(); + + db::write_and_log(cx, || { + KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string()) + }); +} + +pub struct WelcomePage { + workspace: WeakViewHandle, + _settings_subscription: Subscription, +} + +impl Entity for WelcomePage { + type Event = (); +} + +impl View for WelcomePage { + fn ui_name() -> &'static str { + "WelcomePage" + } + + fn render(&mut self, cx: &mut gpui::ViewContext) -> AnyElement { + let self_handle = cx.handle(); + let theme = theme::current(cx); + let width = theme.welcome.page_width; + + let telemetry_settings = *settings::get::(cx); + let vim_mode_setting = settings::get::(cx).0; + + enum Metrics {} + enum Diagnostics {} + + PaneBackdrop::new( + self_handle.id(), + Flex::column() + .with_child( + Flex::column() + .with_child( + theme::ui::svg(&theme.welcome.logo) + .aligned() + .contained() + .aligned(), + ) + .with_child( + Label::new( + "Code at the speed of thought", + theme.welcome.logo_subheading.text.clone(), + ) + .aligned() + .contained() + .with_style(theme.welcome.logo_subheading.container), + ) + .contained() + .with_style(theme.welcome.heading_group) + .constrained() + .with_width(width), + ) + .with_child( + Flex::column() + .with_child(theme::ui::cta_button::( + "Choose a theme", + width, + &theme.welcome.button, + cx, + |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + theme_selector::toggle(workspace, &Default::default(), cx) + }) + } + }, + )) + .with_child(theme::ui::cta_button::( + "Choose a keymap", + width, + &theme.welcome.button, + cx, + |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + base_keymap_picker::toggle( + workspace, + &Default::default(), + cx, + ) + }) + } + }, + )) + .with_child(theme::ui::cta_button::( + "Install the CLI", + width, + &theme.welcome.button, + cx, + |_, _, cx| { + cx.app_context() + .spawn(|cx| async move { install_cli::install_cli(&cx).await }) + .detach_and_log_err(cx); + }, + )) + .contained() + .with_style(theme.welcome.button_group) + .constrained() + .with_width(width), + ) + .with_child( + Flex::column() + .with_child( + theme::ui::checkbox::( + "Enable vim mode", + &theme.welcome.checkbox, + vim_mode_setting, + 0, + cx, + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file::( + fs, + cx, + move |setting| *setting = Some(checked), + ) + } + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container), + ) + .with_child( + theme::ui::checkbox_with_label::( + Flex::column() + .with_child( + Label::new( + "Send anonymous usage data", + theme.welcome.checkbox.label.text.clone(), + ) + .contained() + .with_style(theme.welcome.checkbox.label.container), + ) + .with_child( + Label::new( + "Help > View Telemetry", + theme.welcome.usage_note.text.clone(), + ) + .contained() + .with_style(theme.welcome.usage_note.container), + ), + &theme.welcome.checkbox, + telemetry_settings.metrics, + 0, + cx, + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file::( + fs, + cx, + move |setting| setting.metrics = Some(checked), + ) + } + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container), + ) + .with_child( + theme::ui::checkbox::( + "Send crash reports", + &theme.welcome.checkbox, + telemetry_settings.diagnostics, + 1, + cx, + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file::( + fs, + cx, + move |setting| setting.diagnostics = Some(checked), + ) + } + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container), + ) + .contained() + .with_style(theme.welcome.checkbox_group) + .constrained() + .with_width(width), + ) + .constrained() + .with_max_width(width) + .contained() + .with_uniform_padding(10.) + .aligned() + .into_any(), + ) + .into_any_named("welcome page") + } +} + +impl WelcomePage { + pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { + WelcomePage { + workspace: workspace.weak_handle(), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + } + } +} + +impl Item for WelcomePage { + fn tab_tooltip_text(&self, _: &AppContext) -> Option> { + Some("Welcome to Zed!".into()) + } + + fn tab_content( + &self, + _detail: Option, + style: &theme::Tab, + _cx: &gpui::AppContext, + ) -> AnyElement { + Flex::row() + .with_child( + Label::new("Welcome to Zed!", style.label.clone()) + .aligned() + .contained(), + ) + .into_any() + } + + fn show_toolbar(&self) -> bool { + false + } + + fn clone_on_split( + &self, + _workspace_id: WorkspaceId, + cx: &mut ViewContext, + ) -> Option { + Some(WelcomePage { + workspace: self.workspace.clone(), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + }) + } +} diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 24648f87f1d69..5aba7faaa02fe 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -66,12 +66,12 @@ shellexpand = "2.1.0" text = { package = "text2", path = "../text2" } terminal_view = { package = "terminal_view2", path = "../terminal_view2" } theme = { package = "theme2", path = "../theme2" } -# theme_selector = { path = "../theme_selector" } +theme_selector = { package = "theme_selector2", path = "../theme_selector2" } util = { path = "../util" } # semantic_index = { path = "../semantic_index" } # vim = { path = "../vim" } workspace = { package = "workspace2", path = "../workspace2" } -# welcome = { path = "../welcome" } +welcome = { package = "welcome2", path = "../welcome2" } zed_actions = {package = "zed_actions2", path = "../zed_actions2"} anyhow.workspace = true async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 9c42badb8517f..648c4108d7eb6 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -8,7 +8,7 @@ use anyhow::{anyhow, Context as _, Result}; use backtrace::Backtrace; use chrono::Utc; use cli::FORCE_CLI_MODE_ENV_VAR_NAME; -use client::UserStore; +use client::{Client, UserStore}; use db::kvp::KEY_VALUE_STORE; use editor::Editor; use fs::RealFs; @@ -36,7 +36,7 @@ use std::{ path::{Path, PathBuf}, sync::{ atomic::{AtomicU32, Ordering}, - Arc, + Arc, Weak, }, thread, }; @@ -99,16 +99,15 @@ fn main() { let listener = Arc::new(listener); let open_listener = listener.clone(); app.on_open_urls(move |urls, _| open_listener.open_urls(&urls)); - app.on_reopen(move |_cx| { - // todo!("workspace") - // if cx.has_global::>() { - // if let Some(app_state) = cx.global::>().upgrade() { - // workspace::open_new(&app_state, cx, |workspace, cx| { - // Editor::new_file(workspace, &Default::default(), cx) - // }) - // .detach(); - // } - // } + app.on_reopen(move |cx| { + if cx.has_global::>() { + if let Some(app_state) = cx.global::>().upgrade() { + workspace::open_new(&app_state, cx, |workspace, cx| { + Editor::new_file(workspace, &Default::default(), cx) + }) + .detach(); + } + } }); app.run(move |cx| { @@ -180,7 +179,6 @@ fn main() { user_store, fs, build_window_options, - // background_actions: todo!("ask Mikayla"), workspace_store, node_runtime, }); @@ -236,7 +234,7 @@ fn main() { } } - let mut _triggered_authentication = false; + let mut triggered_authentication = false; fn open_paths_and_log_errs( paths: &[PathBuf], @@ -266,17 +264,17 @@ fn main() { .detach(); } Ok(Some(OpenRequest::JoinChannel { channel_id: _ })) => { - todo!() - // triggered_authentication = true; - // let app_state = app_state.clone(); - // let client = client.clone(); - // cx.spawn(|mut cx| async move { - // // ignore errors here, we'll show a generic "not signed in" - // let _ = authenticate(client, &cx).await; - // cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx)) - // .await - // }) - // .detach_and_log_err(cx) + triggered_authentication = true; + let app_state = app_state.clone(); + let client = client.clone(); + cx.spawn(|mut cx| async move { + // ignore errors here, we'll show a generic "not signed in" + let _ = authenticate(client, &cx).await; + // cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx)) + // .await + anyhow::Ok(()) + }) + .detach_and_log_err(cx) } Ok(Some(OpenRequest::OpenChannelNotes { channel_id: _ })) => { todo!() @@ -315,23 +313,23 @@ fn main() { }) .detach(); - // if !triggered_authentication { - // cx.spawn(|cx| async move { authenticate(client, &cx).await }) - // .detach_and_log_err(cx); - // } + if !triggered_authentication { + cx.spawn(|cx| async move { authenticate(client, &cx).await }) + .detach_and_log_err(cx); + } }); } -// async fn authenticate(client: Arc, cx: &AsyncAppContext) -> Result<()> { -// if stdout_is_a_pty() { -// if client::IMPERSONATE_LOGIN.is_some() { -// client.authenticate_and_connect(false, &cx).await?; -// } -// } else if client.has_keychain_credentials(&cx) { -// client.authenticate_and_connect(true, &cx).await?; -// } -// Ok::<_, anyhow::Error>(()) -// } +async fn authenticate(client: Arc, cx: &AsyncAppContext) -> Result<()> { + if stdout_is_a_pty() { + if client::IMPERSONATE_LOGIN.is_some() { + client.authenticate_and_connect(false, &cx).await?; + } + } else if client.has_keychain_credentials(&cx) { + client.authenticate_and_connect(true, &cx).await?; + } + Ok::<_, anyhow::Error>(()) +} async fn installation_id() -> Result { let legacy_key_name = "device_id"; @@ -355,11 +353,8 @@ async fn restore_or_create_workspace(app_state: &Arc, mut cx: AsyncApp cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))? .await .log_err(); - } else if matches!(KEY_VALUE_STORE.read_kvp("******* THIS IS A BAD KEY PLEASE UNCOMMENT BELOW TO FIX THIS VERY LONG LINE *******"), Ok(None)) { - // todo!(welcome) - //} else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { - //todo!() - // cx.update(|cx| show_welcome_experience(app_state, cx)); + } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { + cx.update(|cx| show_welcome_experience(app_state, cx)); } else { cx.update(|cx| { workspace::open_new(app_state, cx, |workspace, cx| { diff --git a/script/crate-dep-graph b/script/crate-dep-graph index 25285cc097c01..74ea36683cde4 100755 --- a/script/crate-dep-graph +++ b/script/crate-dep-graph @@ -11,7 +11,7 @@ graph_file=target/crate-graph.html cargo depgraph \ --workspace-only \ --offline \ - --root=zed,cli,collab \ + --root=zed2,cli,collab2 \ --dedup-transitive-deps \ | dot -Tsvg > $graph_file From d927c2f49795d72344153391fc5eb045beaf236b Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 28 Nov 2023 15:18:19 -0800 Subject: [PATCH 2/3] Implement all but the UI --- .../command_palette2/src/command_palette.rs | 48 +- crates/file_finder2/src/file_finder.rs | 11 +- crates/picker2/src/picker2.rs | 28 +- crates/storybook2/src/stories/picker.rs | 10 +- crates/theme_selector2/src/theme_selector.rs | 105 +++-- crates/welcome2/src/base_keymap_picker.rs | 118 +++-- crates/welcome2/src/base_keymap_setting.rs | 6 +- crates/welcome2/src/welcome.rs | 437 +++++++++--------- crates/workspace2/src/modal_layer.rs | 1 - crates/workspace2/src/workspace2.rs | 32 +- crates/zed2/src/main.rs | 100 ++-- 11 files changed, 468 insertions(+), 428 deletions(-) diff --git a/crates/command_palette2/src/command_palette.rs b/crates/command_palette2/src/command_palette.rs index 07b819d3a10c0..a393c519be632 100644 --- a/crates/command_palette2/src/command_palette.rs +++ b/crates/command_palette2/src/command_palette.rs @@ -1,17 +1,17 @@ use collections::{CommandPaletteFilter, HashMap}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, div, prelude::*, Action, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, - FocusableView, Keystroke, ParentElement, Render, Styled, View, ViewContext, VisualContext, - WeakView, + actions, div, prelude::*, Action, AnyElement, AppContext, DismissEvent, Div, EventEmitter, + FocusHandle, FocusableView, Keystroke, ParentElement, Render, Styled, View, ViewContext, + VisualContext, WeakView, }; -use picker::{Picker, PickerDelegate}; +use picker::{simple_picker_match, Picker, PickerDelegate}; use std::{ cmp::{self, Reverse}, sync::Arc, }; -use theme::ActiveTheme; -use ui::{h_stack, v_stack, HighlightedLabel, KeyBinding, StyledExt}; + +use ui::{h_stack, v_stack, HighlightedLabel, KeyBinding}; use util::{ channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL}, ResultExt, @@ -141,8 +141,6 @@ impl CommandPaletteDelegate { } impl PickerDelegate for CommandPaletteDelegate { - type ListItem = Div; - fn placeholder_text(&self) -> Arc { "Execute a command...".into() } @@ -294,32 +292,24 @@ impl PickerDelegate for CommandPaletteDelegate { ix: usize, selected: bool, cx: &mut ViewContext>, - ) -> Self::ListItem { - let colors = cx.theme().colors(); + ) -> AnyElement { let Some(r#match) = self.matches.get(ix) else { - return div(); + return div().into_any(); }; let Some(command) = self.commands.get(r#match.candidate_id) else { - return div(); + return div().into_any(); }; - div() - .px_1() - .text_color(colors.text) - .text_ui() - .bg(colors.ghost_element_background) - .rounded_md() - .when(selected, |this| this.bg(colors.ghost_element_selected)) - .hover(|this| this.bg(colors.ghost_element_hover)) - .child( - h_stack() - .justify_between() - .child(HighlightedLabel::new( - command.name.clone(), - r#match.positions.clone(), - )) - .children(KeyBinding::for_action(&*command.action, cx)), - ) + simple_picker_match(selected, cx, |cx| { + h_stack() + .justify_between() + .child(HighlightedLabel::new( + command.name.clone(), + r#match.positions.clone(), + )) + .children(KeyBinding::for_action(&*command.action, cx)) + .into_any() + }) } } diff --git a/crates/file_finder2/src/file_finder.rs b/crates/file_finder2/src/file_finder.rs index ea578fbb0ebb5..c93d29ffecccc 100644 --- a/crates/file_finder2/src/file_finder.rs +++ b/crates/file_finder2/src/file_finder.rs @@ -2,9 +2,9 @@ use collections::HashMap; use editor::{scroll::autoscroll::Autoscroll, Bias, Editor}; use fuzzy::{CharBag, PathMatch, PathMatchCandidate}; use gpui::{ - actions, div, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, - InteractiveElement, IntoElement, Model, ParentElement, Render, Styled, Task, View, ViewContext, - VisualContext, WeakView, + actions, div, AnyElement, AppContext, DismissEvent, Div, Element, EventEmitter, FocusHandle, + FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render, Styled, Task, + View, ViewContext, VisualContext, WeakView, }; use picker::{Picker, PickerDelegate}; use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId}; @@ -530,8 +530,6 @@ impl FileFinderDelegate { } impl PickerDelegate for FileFinderDelegate { - type ListItem = Div; - fn placeholder_text(&self) -> Arc { "Search project files...".into() } @@ -711,7 +709,7 @@ impl PickerDelegate for FileFinderDelegate { ix: usize, selected: bool, cx: &mut ViewContext>, - ) -> Self::ListItem { + ) -> AnyElement { let path_match = self .matches .get(ix) @@ -735,6 +733,7 @@ impl PickerDelegate for FileFinderDelegate { .child(HighlightedLabel::new(file_name, file_name_positions)) .child(HighlightedLabel::new(full_path, full_path_positions)), ) + .into_any() } } diff --git a/crates/picker2/src/picker2.rs b/crates/picker2/src/picker2.rs index dc6b77c7c7b21..76d902da45edd 100644 --- a/crates/picker2/src/picker2.rs +++ b/crates/picker2/src/picker2.rs @@ -1,7 +1,8 @@ use editor::Editor; use gpui::{ - div, prelude::*, uniform_list, AppContext, Div, FocusHandle, FocusableView, MouseButton, - MouseDownEvent, Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext, + div, prelude::*, uniform_list, AnyElement, AppContext, Div, FocusHandle, FocusableView, + MouseButton, MouseDownEvent, Render, Task, UniformListScrollHandle, View, ViewContext, + WindowContext, }; use std::{cmp, sync::Arc}; use ui::{prelude::*, v_stack, Color, Divider, Label}; @@ -15,8 +16,6 @@ pub struct Picker { } pub trait PickerDelegate: Sized + 'static { - type ListItem: IntoElement; - fn match_count(&self) -> usize; fn selected_index(&self) -> usize; fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext>); @@ -32,7 +31,7 @@ pub trait PickerDelegate: Sized + 'static { ix: usize, selected: bool, cx: &mut ViewContext>, - ) -> Self::ListItem; + ) -> AnyElement; } impl FocusableView for Picker { @@ -257,3 +256,22 @@ impl Render for Picker { }) } } + +pub fn simple_picker_match( + selected: bool, + cx: &mut WindowContext, + children: impl FnOnce(&mut WindowContext) -> AnyElement, +) -> AnyElement { + let colors = cx.theme().colors(); + + div() + .px_1() + .text_color(colors.text) + .text_ui() + .bg(colors.ghost_element_background) + .rounded_md() + .when(selected, |this| this.bg(colors.ghost_element_selected)) + .hover(|this| this.bg(colors.ghost_element_hover)) + .child((children)(cx)) + .into_any() +} diff --git a/crates/storybook2/src/stories/picker.rs b/crates/storybook2/src/stories/picker.rs index ae6a26161bd3a..13822c85459f1 100644 --- a/crates/storybook2/src/stories/picker.rs +++ b/crates/storybook2/src/stories/picker.rs @@ -1,6 +1,7 @@ use fuzzy::StringMatchCandidate; use gpui::{ - div, prelude::*, Div, KeyBinding, Render, SharedString, Styled, Task, View, WindowContext, + div, prelude::*, AnyElement, Div, KeyBinding, Render, SharedString, Styled, Task, View, + WindowContext, }; use picker::{Picker, PickerDelegate}; use std::sync::Arc; @@ -36,8 +37,6 @@ impl Delegate { } impl PickerDelegate for Delegate { - type ListItem = Div; - fn match_count(&self) -> usize { self.candidates.len() } @@ -51,10 +50,10 @@ impl PickerDelegate for Delegate { ix: usize, selected: bool, cx: &mut gpui::ViewContext>, - ) -> Self::ListItem { + ) -> AnyElement { let colors = cx.theme().colors(); let Some(candidate_ix) = self.matches.get(ix) else { - return div(); + return div().into_any(); }; // TASK: Make StringMatchCandidate::string a SharedString let candidate = SharedString::from(self.candidates[*candidate_ix].string.clone()); @@ -70,6 +69,7 @@ impl PickerDelegate for Delegate { .text_color(colors.text_accent) }) .child(candidate) + .into_any() } fn selected_index(&self) -> usize { diff --git a/crates/theme_selector2/src/theme_selector.rs b/crates/theme_selector2/src/theme_selector.rs index 6e660caf51bf4..1f3e6e92a14bf 100644 --- a/crates/theme_selector2/src/theme_selector.rs +++ b/crates/theme_selector2/src/theme_selector.rs @@ -2,39 +2,49 @@ use feature_flags::FeatureFlagAppExt; use fs::Fs; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, div, AppContext, Div, EventEmitter, FocusableView, Manager, Render, SharedString, - View, ViewContext, VisualContext, + actions, div, AnyElement, AppContext, DismissEvent, Element, EventEmitter, FocusableView, + InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, View, + ViewContext, VisualContext, WeakView, }; use picker::{Picker, PickerDelegate}; use settings::{update_settings_file, SettingsStore}; use std::sync::Arc; use theme::{ActiveTheme, Theme, ThemeRegistry, ThemeSettings}; use util::ResultExt; -use workspace::{ui::HighlightedLabel, Workspace}; +use workspace::{ + ui::{HighlightedLabel, StyledExt}, + Workspace, +}; actions!(Toggle, Reload); pub fn init(cx: &mut AppContext) { cx.observe_new_views( - |workspace: &mut Workspace, cx: &mut ViewContext| { + |workspace: &mut Workspace, _cx: &mut ViewContext| { workspace.register_action(toggle); }, - ); + ) + .detach(); } pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { let fs = workspace.app_state().fs.clone(); workspace.toggle_modal(cx, |cx| { - ThemeSelector::new(ThemeSelectorDelegate::new(fs, cx), cx) + ThemeSelector::new( + ThemeSelectorDelegate::new(cx.view().downgrade(), fs, cx), + cx, + ) }); } #[cfg(debug_assertions)] pub fn reload(cx: &mut AppContext) { let current_theme_name = cx.theme().name.clone(); - let registry = cx.global::>(); - registry.clear(); - match registry.get(¤t_theme_name) { + let current_theme = cx.update_global(|registry: &mut ThemeRegistry, _cx| { + registry.clear(); + registry.get(¤t_theme_name) + }); + match current_theme { Ok(theme) => { ThemeSelectorDelegate::set_theme(theme, cx); log::info!("reloaded theme {}", current_theme_name); @@ -49,7 +59,7 @@ pub struct ThemeSelector { picker: View>, } -impl EventEmitter for ThemeSelector {} +impl EventEmitter for ThemeSelector {} impl FocusableView for ThemeSelector { fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { @@ -60,7 +70,7 @@ impl FocusableView for ThemeSelector { impl Render for ThemeSelector { type Element = View>; - fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { self.picker.clone() } } @@ -79,16 +89,22 @@ pub struct ThemeSelectorDelegate { original_theme: Arc, selection_completed: bool, selected_index: usize, + view: WeakView, } impl ThemeSelectorDelegate { - fn new(fs: Arc, cx: &mut ViewContext) -> Self { + fn new( + weak_view: WeakView, + fs: Arc, + cx: &mut ViewContext, + ) -> Self { let original_theme = cx.theme().clone(); let staff_mode = cx.is_staff(); let registry = cx.global::>(); - let mut theme_names = registry.list(staff_mode).collect::>(); - theme_names.sort_unstable_by(|a, b| a.is_light.cmp(&b.is_light).then(a.name.cmp(&b.name))); + let theme_names = registry.list(staff_mode).collect::>(); + //todo!(theme sorting) + // theme_names.sort_unstable_by(|a, b| a.is_light.cmp(&b.is_light).then(a.name.cmp(&b.name))); let matches = theme_names .iter() .map(|meta| StringMatch { @@ -105,12 +121,13 @@ impl ThemeSelectorDelegate { original_theme: original_theme.clone(), selected_index: 0, selection_completed: false, + view: weak_view, }; - this.select_if_matching(&original_theme.meta.name); + this.select_if_matching(&original_theme.name); this } - fn show_selected_theme(&mut self, cx: &mut ViewContext) { + fn show_selected_theme(&mut self, cx: &mut ViewContext>) { if let Some(mat) = self.matches.get(self.selected_index) { let registry = cx.global::>(); match registry.get(&mat.string) { @@ -133,18 +150,16 @@ impl ThemeSelectorDelegate { } fn set_theme(theme: Arc, cx: &mut AppContext) { - cx.update_global::(|store, cx| { + cx.update_global(|store: &mut SettingsStore, cx| { let mut theme_settings = store.get::(None).clone(); - theme_settings.theme = theme; + theme_settings.active_theme = theme; store.override_global(theme_settings); - cx.refresh_windows(); + cx.refresh(); }); } } impl PickerDelegate for ThemeSelectorDelegate { - type ListItem = Div; - fn placeholder_text(&self) -> Arc { "Select Theme...".into() } @@ -153,18 +168,22 @@ impl PickerDelegate for ThemeSelectorDelegate { self.matches.len() } - fn confirm(&mut self, _: bool, cx: &mut ViewContext) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { self.selection_completed = true; - let theme_name = cx.theme().meta.name.clone(); - update_settings_file::(self.fs.clone(), cx, |settings| { - settings.theme = Some(theme_name); + let theme_name = cx.theme().name.clone(); + update_settings_file::(self.fs.clone(), cx, move |settings| { + settings.theme = Some(theme_name.to_string()); }); - cx.emit(Manager::Dismiss); + self.view + .update(cx, |_, cx| { + cx.emit(DismissEvent::Dismiss); + }) + .ok(); } - fn dismissed(&mut self, cx: &mut ViewContext) { + fn dismissed(&mut self, cx: &mut ViewContext>) { if !self.selection_completed { Self::set_theme(self.original_theme.clone(), cx); self.selection_completed = true; @@ -175,7 +194,11 @@ impl PickerDelegate for ThemeSelectorDelegate { self.selected_index } - fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext) { + fn set_selected_index( + &mut self, + ix: usize, + cx: &mut ViewContext>, + ) { self.selected_index = ix; self.show_selected_theme(cx); } @@ -183,17 +206,17 @@ impl PickerDelegate for ThemeSelectorDelegate { fn update_matches( &mut self, query: String, - cx: &mut ViewContext, + cx: &mut ViewContext>, ) -> gpui::Task<()> { - let background = cx.background().clone(); + let background = cx.background_executor().clone(); let candidates = self .theme_names .iter() .enumerate() .map(|(id, meta)| StringMatchCandidate { id, - char_bag: meta.name.as_str().into(), - string: meta.name.clone(), + char_bag: meta.as_ref().into(), + string: meta.to_string(), }) .collect::>(); @@ -222,18 +245,23 @@ impl PickerDelegate for ThemeSelectorDelegate { }; this.update(&mut cx, |this, cx| { - let delegate = this.delegate_mut(); - delegate.matches = matches; - delegate.selected_index = delegate + this.delegate.matches = matches; + this.delegate.selected_index = this + .delegate .selected_index - .min(delegate.matches.len().saturating_sub(1)); - delegate.show_selected_theme(cx); + .min(this.delegate.matches.len().saturating_sub(1)); + this.delegate.show_selected_theme(cx); }) .log_err(); }) } - fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> Self::ListItem { + fn render_match( + &self, + ix: usize, + selected: bool, + cx: &mut ViewContext>, + ) -> AnyElement { let theme = cx.theme(); let colors = theme.colors(); @@ -250,5 +278,6 @@ impl PickerDelegate for ThemeSelectorDelegate { theme_match.string.clone(), theme_match.positions.clone(), )) + .into_any() } } diff --git a/crates/welcome2/src/base_keymap_picker.rs b/crates/welcome2/src/base_keymap_picker.rs index 021e3b86a0c7c..b90478f960468 100644 --- a/crates/welcome2/src/base_keymap_picker.rs +++ b/crates/welcome2/src/base_keymap_picker.rs @@ -1,22 +1,23 @@ use super::base_keymap_setting::BaseKeymap; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, - elements::{Element as _, Label}, - AppContext, Task, ViewContext, + actions, AppContext, DismissEvent, EventEmitter, FocusableView, IntoElement, Render, Task, + View, ViewContext, VisualContext, WeakView, }; -use picker::{Picker, PickerDelegate, PickerEvent}; +use picker::{simple_picker_match, Picker, PickerDelegate}; use project::Fs; -use settings::update_settings_file; +use settings::{update_settings_file, Settings}; use std::sync::Arc; use util::ResultExt; -use workspace::Workspace; +use workspace::{ui::HighlightedLabel, Workspace}; -actions!(welcome, [ToggleBaseKeymapSelector]); +actions!(ToggleBaseKeymapSelector); pub fn init(cx: &mut AppContext) { - cx.add_action(toggle); - BaseKeymapSelector::init(cx); + cx.observe_new_views(|workspace: &mut Workspace, _cx| { + workspace.register_action(toggle); + }) + .detach(); } pub fn toggle( @@ -24,28 +25,70 @@ pub fn toggle( _: &ToggleBaseKeymapSelector, cx: &mut ViewContext, ) { - workspace.toggle_modal(cx, |workspace, cx| { - let fs = workspace.app_state().fs.clone(); - cx.add_view(|cx| BaseKeymapSelector::new(BaseKeymapSelectorDelegate::new(fs, cx), cx)) + let fs = workspace.app_state().fs.clone(); + workspace.toggle_modal(cx, |cx| { + BaseKeymapSelector::new( + BaseKeymapSelectorDelegate::new(cx.view().downgrade(), fs, cx), + cx, + ) }); } -pub type BaseKeymapSelector = Picker; +pub struct BaseKeymapSelector { + focus_handle: gpui::FocusHandle, + picker: View>, +} + +impl FocusableView for BaseKeymapSelector { + fn focus_handle(&self, _cx: &AppContext) -> gpui::FocusHandle { + self.focus_handle.clone() + } +} + +impl EventEmitter for BaseKeymapSelector {} + +impl BaseKeymapSelector { + pub fn new( + delegate: BaseKeymapSelectorDelegate, + cx: &mut ViewContext, + ) -> Self { + let picker = cx.build_view(|cx| Picker::new(delegate, cx)); + let focus_handle = cx.focus_handle(); + Self { + focus_handle, + picker, + } + } +} + +impl Render for BaseKeymapSelector { + type Element = View>; + + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { + self.picker.clone() + } +} pub struct BaseKeymapSelectorDelegate { + view: WeakView, matches: Vec, selected_index: usize, fs: Arc, } impl BaseKeymapSelectorDelegate { - fn new(fs: Arc, cx: &mut ViewContext) -> Self { - let base = settings::get::(cx); + fn new( + weak_view: WeakView, + fs: Arc, + cx: &mut ViewContext, + ) -> Self { + let base = BaseKeymap::get(None, cx); let selected_index = BaseKeymap::OPTIONS .iter() .position(|(_, value)| value == base) .unwrap_or(0); Self { + view: weak_view, matches: Vec::new(), selected_index, fs, @@ -66,16 +109,20 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { self.selected_index } - fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext) { + fn set_selected_index( + &mut self, + ix: usize, + _: &mut ViewContext>, + ) { self.selected_index = ix; } fn update_matches( &mut self, query: String, - cx: &mut ViewContext, + cx: &mut ViewContext>, ) -> Task<()> { - let background = cx.background().clone(); + let background = cx.background_executor().clone(); let candidates = BaseKeymap::names() .enumerate() .map(|(id, name)| StringMatchCandidate { @@ -110,43 +157,44 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { }; this.update(&mut cx, |this, _| { - let delegate = this.delegate_mut(); - delegate.matches = matches; - delegate.selected_index = delegate + this.delegate.matches = matches; + this.delegate.selected_index = this + .delegate .selected_index - .min(delegate.matches.len().saturating_sub(1)); + .min(this.delegate.matches.len().saturating_sub(1)); }) .log_err(); }) } - fn confirm(&mut self, _: bool, cx: &mut ViewContext) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { if let Some(selection) = self.matches.get(self.selected_index) { let base_keymap = BaseKeymap::from_names(&selection.string); update_settings_file::(self.fs.clone(), cx, move |setting| { *setting = Some(base_keymap) }); } - cx.emit(PickerEvent::Dismiss); + + self.view + .update(cx, |_, cx| { + cx.emit(DismissEvent::Dismiss); + }) + .ok(); } - fn dismissed(&mut self, _cx: &mut ViewContext) {} + fn dismissed(&mut self, _cx: &mut ViewContext>) {} fn render_match( &self, ix: usize, - mouse_state: &mut gpui::MouseState, selected: bool, - cx: &gpui::AppContext, - ) -> gpui::AnyElement> { - let theme = &theme::current(cx); + cx: &mut gpui::ViewContext>, + ) -> gpui::AnyElement { let keymap_match = &self.matches[ix]; - let style = theme.picker.item.in_state(selected).style_for(mouse_state); - Label::new(keymap_match.string.clone(), style.label.clone()) - .with_highlights(keymap_match.positions.clone()) - .contained() - .with_style(style.container) - .into_any() + simple_picker_match(selected, cx, |_cx| { + HighlightedLabel::new(keymap_match.string.clone(), keymap_match.positions.clone()) + .into_any_element() + }) } } diff --git a/crates/welcome2/src/base_keymap_setting.rs b/crates/welcome2/src/base_keymap_setting.rs index c5b6171f9b4fb..cad6e894f95d7 100644 --- a/crates/welcome2/src/base_keymap_setting.rs +++ b/crates/welcome2/src/base_keymap_setting.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::Setting; +use settings::Settings; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] pub enum BaseKeymap { @@ -44,7 +44,7 @@ impl BaseKeymap { } } -impl Setting for BaseKeymap { +impl Settings for BaseKeymap { const KEY: Option<&'static str> = Some("base_keymap"); type FileContent = Option; @@ -52,7 +52,7 @@ impl Setting for BaseKeymap { fn load( default_value: &Self::FileContent, user_values: &[&Self::FileContent], - _: &gpui::AppContext, + _: &mut gpui::AppContext, ) -> anyhow::Result where Self: Sized, diff --git a/crates/welcome2/src/welcome.rs b/crates/welcome2/src/welcome.rs index a5d95429bdf00..68b515f577e69 100644 --- a/crates/welcome2/src/welcome.rs +++ b/crates/welcome2/src/welcome.rs @@ -1,19 +1,18 @@ mod base_keymap_picker; mod base_keymap_setting; -use crate::base_keymap_picker::ToggleBaseKeymapSelector; -use client::TelemetrySettings; use db::kvp::KEY_VALUE_STORE; use gpui::{ - elements::{Flex, Label, ParentElement}, - AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, WeakViewHandle, + div, red, AnyElement, AppContext, Div, Element, EventEmitter, FocusHandle, Focusable, + FocusableView, InteractiveElement, ParentElement, Render, Styled, Subscription, View, + ViewContext, VisualContext, WeakView, WindowContext, }; -use settings::{update_settings_file, SettingsStore}; -use std::{borrow::Cow, sync::Arc}; -use vim::VimModeSetting; +use settings::{Settings, SettingsStore}; +use std::sync::Arc; use workspace::{ - dock::DockPosition, item::Item, open_new, AppState, PaneBackdrop, Welcome, Workspace, - WorkspaceId, + dock::DockPosition, + item::{Item, ItemEvent}, + open_new, AppState, Welcome, Workspace, WorkspaceId, }; pub use base_keymap_setting::BaseKeymap; @@ -21,12 +20,15 @@ pub use base_keymap_setting::BaseKeymap; pub const FIRST_OPEN: &str = "first_open"; pub fn init(cx: &mut AppContext) { - settings::register::(cx); + BaseKeymap::register(cx); - cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { - let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); - workspace.add_item(Box::new(welcome_page), cx) - }); + cx.observe_new_views(|workspace: &mut Workspace, _cx| { + workspace.register_action(|workspace, _: &Welcome, cx| { + let welcome_page = cx.build_view(|cx| WelcomePage::new(workspace, cx)); + workspace.add_item(Box::new(welcome_page), cx) + }); + }) + .detach(); base_keymap_picker::init(cx); } @@ -34,9 +36,9 @@ pub fn init(cx: &mut AppContext) { pub fn show_welcome_experience(app_state: &Arc, cx: &mut AppContext) { open_new(&app_state, cx, |workspace, cx| { workspace.toggle_dock(DockPosition::Left, cx); - let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); + let welcome_page = cx.build_view(|cx| WelcomePage::new(workspace, cx)); workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); - cx.focus(&welcome_page); + cx.focus_view(&welcome_page); cx.notify(); }) .detach(); @@ -47,227 +49,217 @@ pub fn show_welcome_experience(app_state: &Arc, cx: &mut AppContext) { } pub struct WelcomePage { - workspace: WeakViewHandle, + workspace: WeakView, + focus_handle: FocusHandle, _settings_subscription: Subscription, } -impl Entity for WelcomePage { - type Event = (); -} - -impl View for WelcomePage { - fn ui_name() -> &'static str { - "WelcomePage" - } - - fn render(&mut self, cx: &mut gpui::ViewContext) -> AnyElement { - let self_handle = cx.handle(); - let theme = theme::current(cx); - let width = theme.welcome.page_width; +impl Render for WelcomePage { + type Element = Focusable
; - let telemetry_settings = *settings::get::(cx); - let vim_mode_setting = settings::get::(cx).0; + fn render(&mut self, _cx: &mut gpui::ViewContext) -> Self::Element { + // let self_handle = cx.handle(); + // let theme = cx.theme(); + // let width = theme.welcome.page_width; - enum Metrics {} - enum Diagnostics {} + // let telemetry_settings = TelemetrySettings::get(None, cx); + // let vim_mode_setting = VimModeSettings::get(cx); - PaneBackdrop::new( - self_handle.id(), - Flex::column() - .with_child( - Flex::column() - .with_child( - theme::ui::svg(&theme.welcome.logo) - .aligned() - .contained() - .aligned(), - ) - .with_child( - Label::new( - "Code at the speed of thought", - theme.welcome.logo_subheading.text.clone(), - ) - .aligned() - .contained() - .with_style(theme.welcome.logo_subheading.container), - ) - .contained() - .with_style(theme.welcome.heading_group) - .constrained() - .with_width(width), - ) - .with_child( - Flex::column() - .with_child(theme::ui::cta_button::( - "Choose a theme", - width, - &theme.welcome.button, - cx, - |_, this, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - workspace.update(cx, |workspace, cx| { - theme_selector::toggle(workspace, &Default::default(), cx) - }) - } - }, - )) - .with_child(theme::ui::cta_button::( - "Choose a keymap", - width, - &theme.welcome.button, - cx, - |_, this, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - workspace.update(cx, |workspace, cx| { - base_keymap_picker::toggle( - workspace, - &Default::default(), - cx, - ) - }) - } - }, - )) - .with_child(theme::ui::cta_button::( - "Install the CLI", - width, - &theme.welcome.button, - cx, - |_, _, cx| { - cx.app_context() - .spawn(|cx| async move { install_cli::install_cli(&cx).await }) - .detach_and_log_err(cx); - }, - )) - .contained() - .with_style(theme.welcome.button_group) - .constrained() - .with_width(width), - ) - .with_child( - Flex::column() - .with_child( - theme::ui::checkbox::( - "Enable vim mode", - &theme.welcome.checkbox, - vim_mode_setting, - 0, - cx, - |this, checked, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - let fs = workspace.read(cx).app_state().fs.clone(); - update_settings_file::( - fs, - cx, - move |setting| *setting = Some(checked), - ) - } - }, - ) - .contained() - .with_style(theme.welcome.checkbox_container), - ) - .with_child( - theme::ui::checkbox_with_label::( - Flex::column() - .with_child( - Label::new( - "Send anonymous usage data", - theme.welcome.checkbox.label.text.clone(), - ) - .contained() - .with_style(theme.welcome.checkbox.label.container), - ) - .with_child( - Label::new( - "Help > View Telemetry", - theme.welcome.usage_note.text.clone(), - ) - .contained() - .with_style(theme.welcome.usage_note.container), - ), - &theme.welcome.checkbox, - telemetry_settings.metrics, - 0, - cx, - |this, checked, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - let fs = workspace.read(cx).app_state().fs.clone(); - update_settings_file::( - fs, - cx, - move |setting| setting.metrics = Some(checked), - ) - } - }, - ) - .contained() - .with_style(theme.welcome.checkbox_container), - ) - .with_child( - theme::ui::checkbox::( - "Send crash reports", - &theme.welcome.checkbox, - telemetry_settings.diagnostics, - 1, - cx, - |this, checked, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - let fs = workspace.read(cx).app_state().fs.clone(); - update_settings_file::( - fs, - cx, - move |setting| setting.diagnostics = Some(checked), - ) - } - }, - ) - .contained() - .with_style(theme.welcome.checkbox_container), - ) - .contained() - .with_style(theme.welcome.checkbox_group) - .constrained() - .with_width(width), - ) - .constrained() - .with_max_width(width) - .contained() - .with_uniform_padding(10.) - .aligned() - .into_any(), - ) - .into_any_named("welcome page") + div() + .track_focus(&self.focus_handle) + .child(div().size_full().bg(red()).child("Welcome!")) + //todo!() + // PaneBackdrop::new( + // self_handle.id(), + // Flex::column() + // .with_child( + // Flex::column() + // .with_child( + // theme::ui::svg(&theme.welcome.logo) + // .aligned() + // .contained() + // .aligned(), + // ) + // .with_child( + // Label::new( + // "Code at the speed of thought", + // theme.welcome.logo_subheading.text.clone(), + // ) + // .aligned() + // .contained() + // .with_style(theme.welcome.logo_subheading.container), + // ) + // .contained() + // .with_style(theme.welcome.heading_group) + // .constrained() + // .with_width(width), + // ) + // .with_child( + // Flex::column() + // .with_child(theme::ui::cta_button::( + // "Choose a theme", + // width, + // &theme.welcome.button, + // cx, + // |_, this, cx| { + // if let Some(workspace) = this.workspace.upgrade(cx) { + // workspace.update(cx, |workspace, cx| { + // theme_selector::toggle(workspace, &Default::default(), cx) + // }) + // } + // }, + // )) + // .with_child(theme::ui::cta_button::( + // "Choose a keymap", + // width, + // &theme.welcome.button, + // cx, + // |_, this, cx| { + // if let Some(workspace) = this.workspace.upgrade(cx) { + // workspace.update(cx, |workspace, cx| { + // base_keymap_picker::toggle( + // workspace, + // &Default::default(), + // cx, + // ) + // }) + // } + // }, + // )) + // .with_child(theme::ui::cta_button::( + // "Install the CLI", + // width, + // &theme.welcome.button, + // cx, + // |_, _, cx| { + // cx.app_context() + // .spawn(|cx| async move { install_cli::install_cli(&cx).await }) + // .detach_and_log_err(cx); + // }, + // )) + // .contained() + // .with_style(theme.welcome.button_group) + // .constrained() + // .with_width(width), + // ) + // .with_child( + // Flex::column() + // .with_child( + // theme::ui::checkbox::( + // "Enable vim mode", + // &theme.welcome.checkbox, + // vim_mode_setting, + // 0, + // cx, + // |this, checked, cx| { + // if let Some(workspace) = this.workspace.upgrade(cx) { + // let fs = workspace.read(cx).app_state().fs.clone(); + // update_settings_file::( + // fs, + // cx, + // move |setting| *setting = Some(checked), + // ) + // } + // }, + // ) + // .contained() + // .with_style(theme.welcome.checkbox_container), + // ) + // .with_child( + // theme::ui::checkbox_with_label::( + // Flex::column() + // .with_child( + // Label::new( + // "Send anonymous usage data", + // theme.welcome.checkbox.label.text.clone(), + // ) + // .contained() + // .with_style(theme.welcome.checkbox.label.container), + // ) + // .with_child( + // Label::new( + // "Help > View Telemetry", + // theme.welcome.usage_note.text.clone(), + // ) + // .contained() + // .with_style(theme.welcome.usage_note.container), + // ), + // &theme.welcome.checkbox, + // telemetry_settings.metrics, + // 0, + // cx, + // |this, checked, cx| { + // if let Some(workspace) = this.workspace.upgrade(cx) { + // let fs = workspace.read(cx).app_state().fs.clone(); + // update_settings_file::( + // fs, + // cx, + // move |setting| setting.metrics = Some(checked), + // ) + // } + // }, + // ) + // .contained() + // .with_style(theme.welcome.checkbox_container), + // ) + // .with_child( + // theme::ui::checkbox::( + // "Send crash reports", + // &theme.welcome.checkbox, + // telemetry_settings.diagnostics, + // 1, + // cx, + // |this, checked, cx| { + // if let Some(workspace) = this.workspace.upgrade(cx) { + // let fs = workspace.read(cx).app_state().fs.clone(); + // update_settings_file::( + // fs, + // cx, + // move |setting| setting.diagnostics = Some(checked), + // ) + // } + // }, + // ) + // .contained() + // .with_style(theme.welcome.checkbox_container), + // ) + // .contained() + // .with_style(theme.welcome.checkbox_group) + // .constrained() + // .with_width(width), + // ) + // .constrained() + // .with_max_width(width) + // .contained() + // .with_uniform_padding(10.) + // .aligned() + // .into_any(), + // ) + // .into_any_named("welcome page") } } impl WelcomePage { pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { WelcomePage { + focus_handle: cx.focus_handle(), workspace: workspace.weak_handle(), - _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), } } } -impl Item for WelcomePage { - fn tab_tooltip_text(&self, _: &AppContext) -> Option> { - Some("Welcome to Zed!".into()) +impl EventEmitter for WelcomePage {} + +impl FocusableView for WelcomePage { + fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle { + self.focus_handle.clone() } +} - fn tab_content( - &self, - _detail: Option, - style: &theme::Tab, - _cx: &gpui::AppContext, - ) -> AnyElement { - Flex::row() - .with_child( - Label::new("Welcome to Zed!", style.label.clone()) - .aligned() - .contained(), - ) - .into_any() +impl Item for WelcomePage { + fn tab_content(&self, _: Option, _: &WindowContext) -> AnyElement { + "Welcome to Zed!".into_any() } fn show_toolbar(&self) -> bool { @@ -278,10 +270,11 @@ impl Item for WelcomePage { &self, _workspace_id: WorkspaceId, cx: &mut ViewContext, - ) -> Option { - Some(WelcomePage { + ) -> Option> { + Some(cx.build_view(|cx| WelcomePage { + focus_handle: cx.focus_handle(), workspace: self.workspace.clone(), - _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), - }) + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + })) } } diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index 6d28a6299b9f6..6b14151e0901b 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -46,7 +46,6 @@ impl ModalLayer { previous_focus_handle: cx.focused(), focus_handle: cx.focus_handle(), }); - dbg!("focusing"); cx.focus_view(&new_modal); cx.notify(); } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 50f8611c4cc64..5d326d3c53cb0 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -1808,22 +1808,22 @@ impl Workspace { pane } - // pub fn add_item_to_center( - // &mut self, - // item: Box, - // cx: &mut ViewContext, - // ) -> bool { - // if let Some(center_pane) = self.last_active_center_pane.clone() { - // if let Some(center_pane) = center_pane.upgrade(cx) { - // center_pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx)); - // true - // } else { - // false - // } - // } else { - // false - // } - // } + pub fn add_item_to_center( + &mut self, + item: Box, + cx: &mut ViewContext, + ) -> bool { + if let Some(center_pane) = self.last_active_center_pane.clone() { + if let Some(center_pane) = center_pane.upgrade() { + center_pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx)); + true + } else { + false + } + } else { + false + } + } pub fn add_item(&mut self, item: Box, cx: &mut ViewContext) { self.active_pane diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 843dd00e9feda..4d9a61a52f8da 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -13,7 +13,7 @@ use db::kvp::KEY_VALUE_STORE; use editor::Editor; use fs::RealFs; use futures::StreamExt; -use gpui::{Action, App, AppContext, AsyncAppContext, Context, SemanticVersion, Task}; +use gpui::{App, AppContext, AsyncAppContext, Context, SemanticVersion, Task}; use isahc::{prelude::Configurable, Request}; use language::LanguageRegistry; use log::LevelFilter; @@ -48,6 +48,7 @@ use util::{ paths, ResultExt, }; use uuid::Uuid; +use welcome::{show_welcome_experience, FIRST_OPEN}; use workspace::{AppState, WorkspaceStore}; use zed2::{ build_window_options, ensure_only_instance, handle_cli_connection, initialize_workspace, @@ -163,17 +164,16 @@ fn main() { // assistant::init(cx); // component_test::init(cx); - // cx.spawn(|cx| watch_themes(fs.clone(), cx)).detach(); // cx.spawn(|_| watch_languages(fs.clone(), languages.clone())) // .detach(); // watch_file_types(fs.clone(), cx); languages.set_theme(cx.theme().clone()); - // cx.observe_global::({ - // let languages = languages.clone(); - // move |cx| languages.set_theme(theme::current(cx).clone()) - // }) - // .detach(); + cx.observe_global::({ + let languages = languages.clone(); + move |cx| languages.set_theme(cx.theme().clone()) + }) + .detach(); client.telemetry().start(installation_id, session_id, cx); let telemetry_settings = *client::TelemetrySettings::get_global(cx); @@ -217,14 +217,13 @@ fn main() { // journal2::init(app_state.clone(), cx); // language_selector::init(cx); - // theme_selector::init(cx); + theme_selector::init(cx); // activity_indicator::init(cx); // language_tools::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); collab_ui::init(&app_state, cx); // feedback::init(cx); - // welcome::init(cx); - // zed::init(&app_state, cx); + welcome::init(cx); // cx.set_menus(menus::menus()); initialize_workspace(app_state.clone(), cx); @@ -283,6 +282,7 @@ fn main() { cx.spawn(|mut cx| async move { // ignore errors here, we'll show a generic "not signed in" let _ = authenticate(client, &cx).await; + //todo!() // cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx)) // .await anyhow::Ok(()) @@ -367,7 +367,8 @@ async fn restore_or_create_workspace(app_state: &Arc, mut cx: AsyncApp .await .log_err(); } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { - cx.update(|cx| show_welcome_experience(app_state, cx)); + cx.update(|cx| show_welcome_experience(app_state, cx)) + .log_err(); } else { cx.update(|cx| { workspace::open_new(app_state, cx, |workspace, cx| { @@ -705,44 +706,23 @@ fn load_embedded_fonts(cx: &AppContext) { .unwrap(); } -// #[cfg(debug_assertions)] -// async fn watch_themes(fs: Arc, mut cx: AsyncAppContext) -> Option<()> { -// let mut events = fs -// .watch("styles/src".as_ref(), Duration::from_millis(100)) -// .await; -// while (events.next().await).is_some() { -// let output = Command::new("npm") -// .current_dir("styles") -// .args(["run", "build"]) -// .output() -// .await -// .log_err()?; -// if output.status.success() { -// cx.update(|cx| theme_selector::reload(cx)) -// } else { -// eprintln!( -// "build script failed {}", -// String::from_utf8_lossy(&output.stderr) -// ); -// } -// } -// Some(()) -// } - -// #[cfg(debug_assertions)] -// async fn watch_languages(fs: Arc, languages: Arc) -> Option<()> { -// let mut events = fs -// .watch( -// "crates/zed/src/languages".as_ref(), -// Duration::from_millis(100), -// ) -// .await; -// while (events.next().await).is_some() { -// languages.reload(); -// } -// Some(()) -// } +#[cfg(debug_assertions)] +async fn watch_languages(fs: Arc, languages: Arc) -> Option<()> { + use std::time::Duration; + + let mut events = fs + .watch( + "crates/zed2/src/languages".as_ref(), + Duration::from_millis(100), + ) + .await; + while (events.next().await).is_some() { + languages.reload(); + } + Some(()) +} +//todo!() // #[cfg(debug_assertions)] // fn watch_file_types(fs: Arc, cx: &mut AppContext) { // cx.spawn(|mut cx| async move { @@ -763,26 +743,10 @@ fn load_embedded_fonts(cx: &AppContext) { // .detach() // } -// #[cfg(not(debug_assertions))] -// async fn watch_themes(_fs: Arc, _cx: AsyncAppContext) -> Option<()> { -// None -// } - -// #[cfg(not(debug_assertions))] -// async fn watch_languages(_: Arc, _: Arc) -> Option<()> { -// None -// +#[cfg(not(debug_assertions))] +async fn watch_languages(_: Arc, _: Arc) -> Option<()> { + None +} // #[cfg(not(debug_assertions))] // fn watch_file_types(_fs: Arc, _cx: &mut AppContext) {} - -pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] { - // &[ - // ("Go to file", &file_finder::Toggle), - // ("Open command palette", &command_palette::Toggle), - // ("Open recent projects", &recent_projects::OpenRecent), - // ("Change your settings", &zed_actions::OpenSettings), - // ] - // todo!() - &[] -} From ed8e62cd18c41dc20b548b60bebc37c75296ab53 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 28 Nov 2023 15:25:28 -0800 Subject: [PATCH 3/3] Restore welcome page and several pickers --- .../project_panel2/src/file_associations.rs | 71 ++++++++----------- crates/project_panel2/src/project_panel.rs | 6 +- crates/welcome2/src/welcome.rs | 1 + crates/zed2/src/main.rs | 48 +++++++------ 4 files changed, 60 insertions(+), 66 deletions(-) diff --git a/crates/project_panel2/src/file_associations.rs b/crates/project_panel2/src/file_associations.rs index 9e9a865f3e78d..82aebe7913133 100644 --- a/crates/project_panel2/src/file_associations.rs +++ b/crates/project_panel2/src/file_associations.rs @@ -41,56 +41,47 @@ impl FileAssociations { }) } - pub fn get_icon(path: &Path, cx: &AppContext) -> Arc { - maybe!({ - let this = cx.has_global::().then(|| cx.global::())?; + pub fn get_icon(path: &Path, cx: &AppContext) -> Option> { + let this = cx.has_global::().then(|| cx.global::())?; - // FIXME: Associate a type with the languages and have the file's langauge - // override these associations - maybe!({ - let suffix = path.icon_suffix()?; + // FIXME: Associate a type with the languages and have the file's langauge + // override these associations + maybe!({ + let suffix = path.icon_suffix()?; - this.suffixes - .get(suffix) - .and_then(|type_str| this.types.get(type_str)) - .map(|type_config| type_config.icon.clone()) - }) - .or_else(|| this.types.get("default").map(|config| config.icon.clone())) + this.suffixes + .get(suffix) + .and_then(|type_str| this.types.get(type_str)) + .map(|type_config| type_config.icon.clone()) }) - .unwrap_or_else(|| Arc::from("".to_string())) + .or_else(|| this.types.get("default").map(|config| config.icon.clone())) } - pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Arc { - maybe!({ - let this = cx.has_global::().then(|| cx.global::())?; + pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Option> { + let this = cx.has_global::().then(|| cx.global::())?; - let key = if expanded { - EXPANDED_DIRECTORY_TYPE - } else { - COLLAPSED_DIRECTORY_TYPE - }; + let key = if expanded { + EXPANDED_DIRECTORY_TYPE + } else { + COLLAPSED_DIRECTORY_TYPE + }; - this.types - .get(key) - .map(|type_config| type_config.icon.clone()) - }) - .unwrap_or_else(|| Arc::from("".to_string())) + this.types + .get(key) + .map(|type_config| type_config.icon.clone()) } - pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Arc { - maybe!({ - let this = cx.has_global::().then(|| cx.global::())?; + pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Option> { + let this = cx.has_global::().then(|| cx.global::())?; - let key = if expanded { - EXPANDED_CHEVRON_TYPE - } else { - COLLAPSED_CHEVRON_TYPE - }; + let key = if expanded { + EXPANDED_CHEVRON_TYPE + } else { + COLLAPSED_CHEVRON_TYPE + }; - this.types - .get(key) - .map(|type_config| type_config.icon.clone()) - }) - .unwrap_or_else(|| Arc::from("".to_string())) + this.types + .get(key) + .map(|type_config| type_config.icon.clone()) } } diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index b0272098702f7..c1f30d029f011 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/crates/project_panel2/src/project_panel.rs @@ -1269,16 +1269,16 @@ impl ProjectPanel { let icon = match entry.kind { EntryKind::File(_) => { if show_file_icons { - Some(FileAssociations::get_icon(&entry.path, cx)) + FileAssociations::get_icon(&entry.path, cx) } else { None } } _ => { if show_folder_icons { - Some(FileAssociations::get_folder_icon(is_expanded, cx)) + FileAssociations::get_folder_icon(is_expanded, cx) } else { - Some(FileAssociations::get_chevron_icon(is_expanded, cx)) + FileAssociations::get_chevron_icon(is_expanded, cx) } } }; diff --git a/crates/welcome2/src/welcome.rs b/crates/welcome2/src/welcome.rs index 68b515f577e69..441c2bf696630 100644 --- a/crates/welcome2/src/welcome.rs +++ b/crates/welcome2/src/welcome.rs @@ -58,6 +58,7 @@ impl Render for WelcomePage { type Element = Focusable
; fn render(&mut self, _cx: &mut gpui::ViewContext) -> Self::Element { + // todo!(welcome_ui) // let self_handle = cx.handle(); // let theme = cx.theme(); // let width = theme.welcome.page_width; diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 4d9a61a52f8da..0c65d115d3b20 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -166,7 +166,7 @@ fn main() { // cx.spawn(|_| watch_languages(fs.clone(), languages.clone())) // .detach(); - // watch_file_types(fs.clone(), cx); + watch_file_types(fs.clone(), cx); languages.set_theme(cx.theme().clone()); cx.observe_global::({ @@ -722,31 +722,33 @@ async fn watch_languages(fs: Arc, languages: Arc) Some(()) } -//todo!() -// #[cfg(debug_assertions)] -// fn watch_file_types(fs: Arc, cx: &mut AppContext) { -// cx.spawn(|mut cx| async move { -// let mut events = fs -// .watch( -// "assets/icons/file_icons/file_types.json".as_ref(), -// Duration::from_millis(100), -// ) -// .await; -// while (events.next().await).is_some() { -// cx.update(|cx| { -// cx.update_global(|file_types, _| { -// *file_types = project_panel::file_associations::FileAssociations::new(Assets); -// }); -// }) -// } -// }) -// .detach() -// } +#[cfg(debug_assertions)] +fn watch_file_types(fs: Arc, cx: &mut AppContext) { + use std::time::Duration; + + cx.spawn(|mut cx| async move { + let mut events = fs + .watch( + "assets/icons/file_icons/file_types.json".as_ref(), + Duration::from_millis(100), + ) + .await; + while (events.next().await).is_some() { + cx.update(|cx| { + cx.update_global(|file_types, _| { + *file_types = project_panel::file_associations::FileAssociations::new(Assets); + }); + }) + .ok(); + } + }) + .detach() +} #[cfg(not(debug_assertions))] async fn watch_languages(_: Arc, _: Arc) -> Option<()> { None } -// #[cfg(not(debug_assertions))] -// fn watch_file_types(_fs: Arc, _cx: &mut AppContext) {} +#[cfg(not(debug_assertions))] +fn watch_file_types(_fs: Arc, _cx: &mut AppContext) {}