diff --git a/Cargo.lock b/Cargo.lock index 1291fcaa60..1c61972093 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10923,6 +10923,7 @@ name = "onboarding" version = "0.1.0" dependencies = [ "anyhow", + "client", "command_palette_hooks", "db", "editor", @@ -10937,6 +10938,7 @@ dependencies = [ "theme", "ui", "util", + "vim_mode_setting", "workspace", "workspace-hack", "zed_actions", @@ -18594,7 +18596,6 @@ dependencies = [ "serde", "settings", "telemetry", - "theme", "ui", "util", "vim_mode_setting", diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index b7ba811421..4ad156b9fb 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -126,7 +126,7 @@ impl ChannelMembership { proto::channel_member::Kind::Member => 0, proto::channel_member::Kind::Invitee => 1, }, - username_order: self.user.github_login.as_str(), + username_order: &self.user.github_login, } } } diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index e025ec0523..97fb959171 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -55,7 +55,7 @@ pub struct ParticipantIndex(pub u32); #[derive(Default, Debug)] pub struct User { pub id: UserId, - pub github_login: String, + pub github_login: SharedString, pub avatar_uri: SharedUri, pub name: Option, } @@ -107,7 +107,7 @@ pub enum ContactRequestStatus { pub struct UserStore { users: HashMap>, - by_github_login: HashMap, + by_github_login: HashMap, participant_indices: HashMap, update_contacts_tx: mpsc::UnboundedSender, current_plan: Option, @@ -904,7 +904,7 @@ impl UserStore { let mut missing_user_ids = Vec::new(); for id in user_ids { if let Some(github_login) = self.get_cached_user(id).map(|u| u.github_login.clone()) { - ret.insert(id, github_login.into()); + ret.insert(id, github_login); } else { missing_user_ids.push(id) } @@ -925,7 +925,7 @@ impl User { fn new(message: proto::User) -> Arc { Arc::new(User { id: message.id, - github_login: message.github_login, + github_login: message.github_login.into(), avatar_uri: message.avatar_url.into(), name: message.name, }) diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 19e410de5b..8d5d076780 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -38,12 +38,12 @@ fn room_participants(room: &Entity, cx: &mut TestAppContext) -> RoomPartic let mut remote = room .remote_participants() .values() - .map(|participant| participant.user.github_login.clone()) + .map(|participant| participant.user.github_login.clone().to_string()) .collect::>(); let mut pending = room .pending_participants() .iter() - .map(|user| user.github_login.clone()) + .map(|user| user.github_login.clone().to_string()) .collect::>(); remote.sort(); pending.sort(); diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index f1cc2bf24a..7aa41e0e7d 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -1881,7 +1881,7 @@ async fn test_active_call_events( vec![room::Event::RemoteProjectShared { owner: Arc::new(User { id: client_a.user_id().unwrap(), - github_login: "user_a".to_string(), + github_login: "user_a".into(), avatar_uri: "avatar_a".into(), name: None, }), @@ -1900,7 +1900,7 @@ async fn test_active_call_events( vec![room::Event::RemoteProjectShared { owner: Arc::new(User { id: client_b.user_id().unwrap(), - github_login: "user_b".to_string(), + github_login: "user_b".into(), avatar_uri: "avatar_b".into(), name: None, }), @@ -6079,7 +6079,7 @@ async fn test_contacts( .iter() .map(|contact| { ( - contact.user.github_login.clone(), + contact.user.github_login.clone().to_string(), if contact.online { "online" } else { "offline" }, if contact.busy { "busy" } else { "free" }, ) diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 00d1caa7c5..3751d6918e 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -696,17 +696,17 @@ impl TestClient { current: store .contacts() .iter() - .map(|contact| contact.user.github_login.clone()) + .map(|contact| contact.user.github_login.clone().to_string()) .collect(), outgoing_requests: store .outgoing_contact_requests() .iter() - .map(|user| user.github_login.clone()) + .map(|user| user.github_login.clone().to_string()) .collect(), incoming_requests: store .incoming_contact_requests() .iter() - .map(|user| user.github_login.clone()) + .map(|user| user.github_login.clone().to_string()) .collect(), }) } diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 4d5973481e..f53b94c209 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -940,7 +940,7 @@ impl CollabPanel { room.read(cx).local_participant().role == proto::ChannelRole::Admin }); - ListItem::new(SharedString::from(user.github_login.clone())) + ListItem::new(user.github_login.clone()) .start_slot(Avatar::new(user.avatar_uri.clone())) .child(Label::new(user.github_login.clone())) .toggle_state(is_selected) @@ -2583,7 +2583,7 @@ impl CollabPanel { ) -> impl IntoElement { let online = contact.online; let busy = contact.busy || calling; - let github_login = SharedString::from(contact.user.github_login.clone()); + let github_login = contact.user.github_login.clone(); let item = ListItem::new(github_login.clone()) .indent_level(1) .indent_step_size(px(20.)) @@ -2662,7 +2662,7 @@ impl CollabPanel { is_selected: bool, cx: &mut Context, ) -> impl IntoElement { - let github_login = SharedString::from(user.github_login.clone()); + let github_login = user.github_login.clone(); let user_id = user.id; let is_response_pending = self.user_store.read(cx).is_contact_request_pending(user); let color = if is_response_pending { diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index e196a5b139..ee74ac4d54 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -2416,7 +2416,7 @@ impl GitPanel { .committer_name .clone() .or_else(|| participant.user.name.clone()) - .unwrap_or_else(|| participant.user.github_login.clone()); + .unwrap_or_else(|| participant.user.github_login.clone().to_string()); new_co_authors.push((name.clone(), email.clone())) } } @@ -2436,7 +2436,7 @@ impl GitPanel { .name .clone() .or_else(|| user.name.clone()) - .unwrap_or_else(|| user.github_login.clone()); + .unwrap_or_else(|| user.github_login.clone().to_string()); Some((name, email)) } diff --git a/crates/onboarding/Cargo.toml b/crates/onboarding/Cargo.toml index 04c9fce1dc..c6536afecd 100644 --- a/crates/onboarding/Cargo.toml +++ b/crates/onboarding/Cargo.toml @@ -16,6 +16,7 @@ default = [] [dependencies] anyhow.workspace = true +client.workspace = true command_palette_hooks.workspace = true db.workspace = true editor.workspace = true @@ -30,6 +31,7 @@ settings.workspace = true theme.workspace = true ui.workspace = true util.workspace = true +vim_mode_setting.workspace = true workspace-hack.workspace = true workspace.workspace = true zed_actions.workspace = true diff --git a/crates/onboarding/src/basics_page.rs b/crates/onboarding/src/basics_page.rs index efb2000e06..a57e49977a 100644 --- a/crates/onboarding/src/basics_page.rs +++ b/crates/onboarding/src/basics_page.rs @@ -1,16 +1,28 @@ -use fs::Fs; -use gpui::{App, IntoElement, Window}; -use settings::{Settings, update_settings_file}; -use theme::{ThemeMode, ThemeSettings}; -use ui::{SwitchField, ToggleButtonGroup, ToggleButtonSimple, ToggleButtonWithIcon, prelude::*}; +use std::sync::Arc; -fn read_theme_selection(cx: &App) -> ThemeMode { +use client::TelemetrySettings; +use fs::Fs; +use gpui::{App, IntoElement}; +use settings::{BaseKeymap, Settings, update_settings_file}; +use theme::{Appearance, SystemAppearance, ThemeMode, ThemeSettings}; +use ui::{ + SwitchField, ThemePreviewTile, ToggleButtonGroup, ToggleButtonSimple, ToggleButtonWithIcon, + prelude::*, +}; +use vim_mode_setting::VimModeSetting; + +use crate::Onboarding; + +fn read_theme_selection(cx: &App) -> (ThemeMode, SharedString) { let settings = ThemeSettings::get_global(cx); - settings - .theme_selection - .as_ref() - .and_then(|selection| selection.mode()) - .unwrap_or_default() + ( + settings + .theme_selection + .as_ref() + .and_then(|selection| selection.mode()) + .unwrap_or_default(), + settings.active_theme.name.clone(), + ) } fn write_theme_selection(theme_mode: ThemeMode, cx: &App) { @@ -21,9 +33,15 @@ fn write_theme_selection(theme_mode: ThemeMode, cx: &App) { }); } -fn render_theme_section(cx: &mut App) -> impl IntoElement { - let theme_mode = read_theme_selection(cx); +fn write_keymap_base(keymap_base: BaseKeymap, cx: &App) { + let fs = ::global(cx); + update_settings_file::(fs, cx, move |setting, _| { + *setting = Some(keymap_base); + }); +} + +fn render_theme_section(theme_mode: ThemeMode) -> impl IntoElement { h_flex().justify_between().child(Label::new("Theme")).child( ToggleButtonGroup::single_row( "theme-selector-onboarding", @@ -49,55 +67,160 @@ fn render_theme_section(cx: &mut App) -> impl IntoElement { ) } -fn render_telemetry_section() -> impl IntoElement { +fn render_telemetry_section(fs: Arc, cx: &App) -> impl IntoElement { v_flex() - .gap_3() + + .gap_4() .child(Label::new("Telemetry").size(LabelSize::Large)) .child(SwitchField::new( - "vim_mode", + "onboarding-telemetry-metrics", "Help Improve Zed", "Sending anonymous usage data helps us build the right features and create the best experience.", - ui::ToggleState::Selected, - |_, _, _| {}, + if TelemetrySettings::get_global(cx).metrics { + ui::ToggleState::Selected + } else { + ui::ToggleState::Unselected + }, + { + let fs = fs.clone(); + move |selection, _, cx| { + let enabled = match selection { + ToggleState::Selected => true, + ToggleState::Unselected => false, + ToggleState::Indeterminate => { return; }, + }; + + update_settings_file::( + fs.clone(), + cx, + move |setting, _| setting.metrics = Some(enabled), + ); + }}, )) .child(SwitchField::new( - "vim_mode", + "onboarding-telemetry-crash-reports", "Help Fix Zed", "Send crash reports so we can fix critical issues fast.", - ui::ToggleState::Selected, - |_, _, _| {}, + if TelemetrySettings::get_global(cx).diagnostics { + ui::ToggleState::Selected + } else { + ui::ToggleState::Unselected + }, + { + let fs = fs.clone(); + move |selection, _, cx| { + let enabled = match selection { + ToggleState::Selected => true, + ToggleState::Unselected => false, + ToggleState::Indeterminate => { return; }, + }; + + update_settings_file::( + fs.clone(), + cx, + move |setting, _| setting.diagnostics = Some(enabled), + ); + } + } )) } -pub(crate) fn render_basics_page(_: &mut Window, cx: &mut App) -> impl IntoElement { +pub(crate) fn render_basics_page(onboarding: &Onboarding, cx: &mut App) -> impl IntoElement { + let (theme_mode, active_theme_name) = read_theme_selection(cx); + let themes = match theme_mode { + ThemeMode::Dark => &onboarding.dark_themes, + ThemeMode::Light => &onboarding.light_themes, + ThemeMode::System => match SystemAppearance::global(cx).0 { + Appearance::Light => &onboarding.light_themes, + Appearance::Dark => &onboarding.dark_themes, + }, + }; + + let base_keymap = match BaseKeymap::get_global(cx) { + BaseKeymap::VSCode => Some(0), + BaseKeymap::JetBrains => Some(1), + BaseKeymap::SublimeText => Some(2), + BaseKeymap::Atom => Some(3), + BaseKeymap::Emacs => Some(4), + BaseKeymap::Cursor => Some(5), + BaseKeymap::TextMate | BaseKeymap::None => None, + }; + v_flex() .gap_6() - .child(render_theme_section(cx)) + .child(render_theme_section(theme_mode)) + .child(h_flex().children( + themes.iter().map(|theme| { + ThemePreviewTile::new(theme.clone(), active_theme_name == theme.name, 0.48) + .on_click({ + let theme_name = theme.name.clone(); + let fs = onboarding.fs.clone(); + move |_, _, cx| { + let theme_name = theme_name.clone(); + update_settings_file::(fs.clone(), cx, move |settings, cx| { + settings.set_theme(theme_name.to_string(), SystemAppearance::global(cx).0); + }); + } + }) + }) + )) .child( v_flex().gap_2().child(Label::new("Base Keymap")).child( ToggleButtonGroup::two_rows( "multiple_row_test", [ - ToggleButtonWithIcon::new("VS Code", IconName::AiZed, |_, _, _| {}), - ToggleButtonWithIcon::new("Jetbrains", IconName::AiZed, |_, _, _| {}), - ToggleButtonWithIcon::new("Sublime Text", IconName::AiZed, |_, _, _| {}), + ToggleButtonWithIcon::new("VS Code", IconName::AiZed, |_, _, cx| { + write_keymap_base(BaseKeymap::VSCode, cx); + }), + ToggleButtonWithIcon::new("Jetbrains", IconName::AiZed, |_, _, cx| { + write_keymap_base(BaseKeymap::JetBrains, cx); + }), + ToggleButtonWithIcon::new("Sublime Text", IconName::AiZed, |_, _, cx| { + write_keymap_base(BaseKeymap::SublimeText, cx); + }), ], [ - ToggleButtonWithIcon::new("Atom", IconName::AiZed, |_, _, _| {}), - ToggleButtonWithIcon::new("Emacs", IconName::AiZed, |_, _, _| {}), - ToggleButtonWithIcon::new("Cursor (Beta)", IconName::AiZed, |_, _, _| {}), + ToggleButtonWithIcon::new("Atom", IconName::AiZed, |_, _, cx| { + write_keymap_base(BaseKeymap::Atom, cx); + }), + ToggleButtonWithIcon::new("Emacs", IconName::AiZed, |_, _, cx| { + write_keymap_base(BaseKeymap::Emacs, cx); + }), + ToggleButtonWithIcon::new("Cursor (Beta)", IconName::AiZed, |_, _, cx| { + write_keymap_base(BaseKeymap::Cursor, cx); + }), ], ) + .when_some(base_keymap, |this, base_keymap| this.selected_index(base_keymap)) .button_width(rems_from_px(230.)) .style(ui::ToggleButtonGroupStyle::Outlined) ), ) .child(v_flex().justify_center().child(div().h_0().child("hack").invisible()).child(SwitchField::new( - "vim_mode", + "onboarding-vim-mode", "Vim Mode", "Coming from Neovim? Zed's first-class implementation of Vim Mode has got your back.", - ui::ToggleState::Selected, - |_, _, _| {}, + if VimModeSetting::get_global(cx).0 { + ui::ToggleState::Selected + } else { + ui::ToggleState::Unselected + }, + { + let fs = onboarding.fs.clone(); + move |selection, _, cx| { + let enabled = match selection { + ToggleState::Selected => true, + ToggleState::Unselected => false, + ToggleState::Indeterminate => { return; }, + }; + + update_settings_file::( + fs.clone(), + cx, + move |setting, _| *setting = Some(enabled), + ); + } + }, ))) - .child(render_telemetry_section()) + .child(render_telemetry_section(onboarding.fs.clone(), cx)) } diff --git a/crates/onboarding/src/onboarding.rs b/crates/onboarding/src/onboarding.rs index 69b9301302..9b18119b83 100644 --- a/crates/onboarding/src/onboarding.rs +++ b/crates/onboarding/src/onboarding.rs @@ -1,4 +1,5 @@ use crate::welcome::{ShowWelcome, WelcomePage}; +use client::{Client, UserStore}; use command_palette_hooks::CommandPaletteFilter; use db::kvp::KEY_VALUE_STORE; use feature_flags::{FeatureFlag, FeatureFlagViewExt as _}; @@ -12,11 +13,13 @@ use schemars::JsonSchema; use serde::Deserialize; use settings::{SettingsStore, VsCodeSettingsSource}; use std::sync::Arc; -use ui::{FluentBuilder, KeyBinding, Vector, VectorName, prelude::*, rems_from_px}; +use theme::{Theme, ThemeRegistry}; +use ui::{Avatar, FluentBuilder, KeyBinding, Vector, VectorName, prelude::*, rems_from_px}; use workspace::{ AppState, Workspace, WorkspaceId, dock::DockPosition, item::{Item, ItemEvent}, + notifications::NotifyResultExt as _, open_new, with_active_or_new_workspace, }; @@ -72,7 +75,11 @@ pub fn init(cx: &mut App) { if let Some(existing) = existing { workspace.activate_item(&existing, true, true, window, cx); } else { - let settings_page = Onboarding::new(workspace.weak_handle(), cx); + let settings_page = Onboarding::new( + workspace.weak_handle(), + workspace.user_store().clone(), + cx, + ); workspace.add_item_to_active_pane( Box::new(settings_page), None, @@ -188,7 +195,8 @@ pub fn show_onboarding_view(app_state: Arc, cx: &mut App) -> Task, + light_themes: [Arc; 3], + dark_themes: [Arc; 3], focus_handle: FocusHandle, selected_page: SelectedPage, + fs: Arc, + user_store: Entity, _settings_subscription: Subscription, } impl Onboarding { - fn new(workspace: WeakEntity, cx: &mut App) -> Entity { + fn new( + workspace: WeakEntity, + user_store: Entity, + cx: &mut App, + ) -> Entity { + let theme_registry = ThemeRegistry::global(cx); + + let one_dark = theme_registry + .get("One Dark") + .expect("Default themes are always present"); + let ayu_dark = theme_registry + .get("Ayu Dark") + .expect("Default themes are always present"); + let gruvbox_dark = theme_registry + .get("Gruvbox Dark") + .expect("Default themes are always present"); + + let one_light = theme_registry + .get("One Light") + .expect("Default themes are always present"); + let ayu_light = theme_registry + .get("Ayu Light") + .expect("Default themes are always present"); + let gruvbox_light = theme_registry + .get("Gruvbox Light") + .expect("Default themes are always present"); + cx.new(|cx| Self { workspace, + user_store, focus_handle: cx.focus_handle(), + light_themes: [one_light, ayu_light, gruvbox_light], + dark_themes: [one_dark, ayu_dark, gruvbox_dark], selected_page: SelectedPage::Basics, + fs: ::global(cx), _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), }) } @@ -339,16 +381,37 @@ impl Onboarding { ), ) .child( - Button::new("sign_in", "Sign In") - .style(ButtonStyle::Outlined) - .full_width(), + if let Some(user) = self.user_store.read(cx).current_user() { + h_flex() + .gap_2() + .child(Avatar::new(user.avatar_uri.clone())) + .child(Label::new(user.github_login.clone())) + .into_any_element() + } else { + Button::new("sign_in", "Sign In") + .style(ButtonStyle::Outlined) + .full_width() + .on_click(|_, window, cx| { + let client = Client::global(cx); + window + .spawn(cx, async move |cx| { + client + .authenticate_and_connect(true, &cx) + .await + .into_response() + .notify_async_err(cx); + }) + .detach(); + }) + .into_any_element() + }, ) } fn render_page(&mut self, window: &mut Window, cx: &mut Context) -> AnyElement { match self.selected_page { SelectedPage::Basics => { - crate::basics_page::render_basics_page(window, cx).into_any_element() + crate::basics_page::render_basics_page(&self, cx).into_any_element() } SelectedPage::Editing => { crate::editing_page::render_editing_page(window, cx).into_any_element() @@ -420,7 +483,11 @@ impl Item for Onboarding { _: &mut Window, cx: &mut Context, ) -> Option> { - Some(Onboarding::new(self.workspace.clone(), cx)) + Some(Onboarding::new( + self.workspace.clone(), + self.user_store.clone(), + cx, + )) } fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) { diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index 9c2961c55f..07ea331ef5 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -34,6 +34,7 @@ mod stack; mod sticky_items; mod tab; mod tab_bar; +mod theme_preview; mod toggle; mod tooltip; @@ -76,6 +77,7 @@ pub use stack::*; pub use sticky_items::*; pub use tab::*; pub use tab_bar::*; +pub use theme_preview::*; pub use toggle::*; pub use tooltip::*; diff --git a/crates/ui/src/components/button/toggle_button.rs b/crates/ui/src/components/button/toggle_button.rs index 30683e60f3..a621585349 100644 --- a/crates/ui/src/components/button/toggle_button.rs +++ b/crates/ui/src/components/button/toggle_button.rs @@ -431,15 +431,17 @@ impl RenderOnce { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { let entries = self.rows.into_iter().enumerate().map(|(row_index, row)| { - row.into_iter().enumerate().map(move |(index, button)| { + row.into_iter().enumerate().map(move |(col_index, button)| { let ButtonConfiguration { label, icon, on_click, } = button.into_configuration(); - ButtonLike::new((self.group_name, row_index * COLS + index)) - .when(index == self.selected_index, |this| { + let entry_index = row_index * COLS + col_index; + + ButtonLike::new((self.group_name, entry_index)) + .when(entry_index == self.selected_index, |this| { this.toggle_state(true) .selected_style(ButtonStyle::Tinted(TintColor::Accent)) }) @@ -451,10 +453,12 @@ impl RenderOnce h_flex() .min_w(self.button_width) .gap_1p5() + .px_3() + .py_1() .justify_center() .when_some(icon, |this, icon| { this.child(Icon::new(icon).size(IconSize::XSmall).map(|this| { - if index == self.selected_index { + if entry_index == self.selected_index { this.color(Color::Accent) } else { this.color(Color::Muted) @@ -462,9 +466,11 @@ impl RenderOnce })) }) .child( - Label::new(label).when(index == self.selected_index, |this| { - this.color(Color::Accent) - }), + Label::new(label) + .size(LabelSize::Small) + .when(entry_index == self.selected_index, |this| { + this.color(Color::Accent) + }), ), ) .on_click(on_click) diff --git a/crates/welcome/src/welcome_ui/theme_preview.rs b/crates/ui/src/components/theme_preview.rs similarity index 89% rename from crates/welcome/src/welcome_ui/theme_preview.rs rename to crates/ui/src/components/theme_preview.rs index b3a80c74c3..d2ff778279 100644 --- a/crates/welcome/src/welcome_ui/theme_preview.rs +++ b/crates/ui/src/components/theme_preview.rs @@ -1,10 +1,7 @@ -#![allow(unused, dead_code)] -use gpui::{Hsla, Length}; -use std::sync::Arc; +use crate::{component_prelude::Documented, prelude::*, utils::inner_corner_radius}; +use gpui::{App, ClickEvent, Hsla, IntoElement, Length, RenderOnce, Window}; +use std::{rc::Rc, sync::Arc}; use theme::{Theme, ThemeRegistry}; -use ui::{ - IntoElement, RenderOnce, component_prelude::Documented, prelude::*, utils::inner_corner_radius, -}; /// Shows a preview of a theme as an abstract illustration /// of a thumbnail-sized editor. @@ -12,6 +9,7 @@ use ui::{ pub struct ThemePreviewTile { theme: Arc, selected: bool, + on_click: Option>, seed: f32, } @@ -19,8 +17,9 @@ impl ThemePreviewTile { pub fn new(theme: Arc, selected: bool, seed: f32) -> Self { Self { theme, - selected, seed, + selected, + on_click: None, } } @@ -28,10 +27,18 @@ impl ThemePreviewTile { self.selected = selected; self } + + pub fn on_click( + mut self, + listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, + ) -> Self { + self.on_click = Some(Rc::new(listener)); + self + } } impl RenderOnce for ThemePreviewTile { - fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement { + fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { let color = self.theme.colors(); let root_radius = px(8.0); @@ -181,6 +188,13 @@ impl RenderOnce for ThemePreviewTile { let content = div().size_full().flex().child(sidebar).child(pane); div() + // Note: If two theme preview tiles are rendering the same theme they'll share an ID + // this will mean on hover and on click events will be shared between them + .id(SharedString::from(self.theme.id.clone())) + .when_some(self.on_click.clone(), |this, on_click| { + this.on_click(move |event, window, cx| on_click(event, window, cx)) + .hover(|style| style.cursor_pointer().border_color(color.element_hover)) + }) .size_full() .rounded(root_radius) .p(root_padding) @@ -261,7 +275,7 @@ impl Component for ThemePreviewTile { themes_to_preview .iter() .enumerate() - .map(|(i, theme)| { + .map(|(_, theme)| { div().w(px(200.)).h(px(140.)).child(ThemePreviewTile::new( theme.clone(), false, diff --git a/crates/ui/src/components/toggle.rs b/crates/ui/src/components/toggle.rs index 8209445c70..daa8aa7fbe 100644 --- a/crates/ui/src/components/toggle.rs +++ b/crates/ui/src/components/toggle.rs @@ -1,6 +1,6 @@ use gpui::{ - AnyElement, AnyView, ClickEvent, CursorStyle, ElementId, Hsla, IntoElement, Styled, Window, - div, hsla, prelude::*, + AnyElement, AnyView, ClickEvent, ElementId, Hsla, IntoElement, Styled, Window, div, hsla, + prelude::*, }; use std::sync::Arc; @@ -610,7 +610,7 @@ impl RenderOnce for SwitchField { h_flex() .id(SharedString::from(format!("{}-container", self.id))) .when(!self.disabled, |this| { - this.hover(|this| this.cursor(CursorStyle::PointingHand)) + this.hover(|this| this.cursor_pointer()) }) .w_full() .gap_4() diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index 769dd8d6aa..acb3fe0f84 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -29,7 +29,6 @@ project.workspace = true serde.workspace = true settings.workspace = true telemetry.workspace = true -theme.workspace = true ui.workspace = true util.workspace = true vim_mode_setting.workspace = true diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 49bf2031ab..352118eee8 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -21,7 +21,6 @@ pub use multibuffer_hint::*; mod base_keymap_picker; mod multibuffer_hint; -mod welcome_ui; actions!( welcome, diff --git a/crates/welcome/src/welcome_ui.rs b/crates/welcome/src/welcome_ui.rs deleted file mode 100644 index 622b6f448d..0000000000 --- a/crates/welcome/src/welcome_ui.rs +++ /dev/null @@ -1 +0,0 @@ -mod theme_preview;