diff --git a/crates/editor/src/editor_settings_controls.rs b/crates/editor/src/editor_settings_controls.rs index c8e396f904..7de2f6a4ec 100644 --- a/crates/editor/src/editor_settings_controls.rs +++ b/crates/editor/src/editor_settings_controls.rs @@ -1,7 +1,7 @@ use gpui::{AppContext, FontWeight}; use project::project_settings::{InlineBlameSettings, ProjectSettings}; use settings::{EditableSettingControl, Settings}; -use theme::ThemeSettings; +use theme::{FontFamilyCache, ThemeSettings}; use ui::{ prelude::*, CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup, @@ -21,13 +21,78 @@ impl RenderOnce for EditorSettingsControls { SettingsContainer::new() .child( SettingsGroup::new("Font") - .child(BufferFontSizeControl) - .child(BufferFontWeightControl), + .child( + h_flex() + .gap_2() + .justify_between() + .child(BufferFontFamilyControl) + .child(BufferFontWeightControl), + ) + .child(BufferFontSizeControl), ) .child(SettingsGroup::new("Editor").child(InlineGitBlameControl)) } } +#[derive(IntoElement)] +struct BufferFontFamilyControl; + +impl EditableSettingControl for BufferFontFamilyControl { + type Value = SharedString; + type Settings = ThemeSettings; + + fn name(&self) -> SharedString { + "Buffer Font Family".into() + } + + fn read(cx: &AppContext) -> Self::Value { + let settings = ThemeSettings::get_global(cx); + settings.buffer_font.family.clone() + } + + fn apply( + settings: &mut ::FileContent, + value: Self::Value, + _cx: &AppContext, + ) { + settings.buffer_font_family = Some(value.to_string()); + } +} + +impl RenderOnce for BufferFontFamilyControl { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let value = Self::read(cx); + + h_flex() + .gap_2() + .child(Icon::new(IconName::Font)) + .child(DropdownMenu::new( + "buffer-font-family", + value.clone(), + ContextMenu::build(cx, |mut menu, cx| { + let font_family_cache = FontFamilyCache::global(cx); + + for font_name in font_family_cache.list_font_families(cx) { + menu = menu.custom_entry( + { + let font_name = font_name.clone(); + move |_cx| Label::new(font_name.clone()).into_any_element() + }, + { + let font_name = font_name.clone(); + move |cx| { + Self::write(font_name.clone(), cx); + } + }, + ) + } + + menu + }), + )) + } +} + #[derive(IntoElement)] struct BufferFontSizeControl; diff --git a/crates/settings_ui/src/appearance_settings_controls.rs b/crates/settings_ui/src/appearance_settings_controls.rs index 599f735301..e1e375a9f3 100644 --- a/crates/settings_ui/src/appearance_settings_controls.rs +++ b/crates/settings_ui/src/appearance_settings_controls.rs @@ -1,6 +1,6 @@ use gpui::{AppContext, FontWeight}; use settings::{EditableSettingControl, Settings}; -use theme::{SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings}; +use theme::{FontFamilyCache, SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings}; use ui::{ prelude::*, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup, ToggleButton, @@ -29,8 +29,14 @@ impl RenderOnce for AppearanceSettingsControls { ) .child( SettingsGroup::new("Font") - .child(UiFontSizeControl) - .child(UiFontWeightControl), + .child( + h_flex() + .gap_2() + .justify_between() + .child(UiFontFamilyControl) + .child(UiFontWeightControl), + ) + .child(UiFontSizeControl), ) } } @@ -159,6 +165,65 @@ impl RenderOnce for ThemeModeControl { } } +#[derive(IntoElement)] +struct UiFontFamilyControl; + +impl EditableSettingControl for UiFontFamilyControl { + type Value = SharedString; + type Settings = ThemeSettings; + + fn name(&self) -> SharedString { + "UI Font Family".into() + } + + fn read(cx: &AppContext) -> Self::Value { + let settings = ThemeSettings::get_global(cx); + settings.ui_font.family.clone() + } + + fn apply( + settings: &mut ::FileContent, + value: Self::Value, + _cx: &AppContext, + ) { + settings.ui_font_family = Some(value.to_string()); + } +} + +impl RenderOnce for UiFontFamilyControl { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let value = Self::read(cx); + + h_flex() + .gap_2() + .child(Icon::new(IconName::Font)) + .child(DropdownMenu::new( + "ui-font-family", + value.clone(), + ContextMenu::build(cx, |mut menu, cx| { + let font_family_cache = FontFamilyCache::global(cx); + + for font_name in font_family_cache.list_font_families(cx) { + menu = menu.custom_entry( + { + let font_name = font_name.clone(); + move |_cx| Label::new(font_name.clone()).into_any_element() + }, + { + let font_name = font_name.clone(); + move |cx| { + Self::write(font_name.clone(), cx); + } + }, + ) + } + + menu + }), + )) + } +} + #[derive(IntoElement)] struct UiFontSizeControl; diff --git a/crates/theme/src/font_family_cache.rs b/crates/theme/src/font_family_cache.rs new file mode 100644 index 0000000000..c9583b9e8d --- /dev/null +++ b/crates/theme/src/font_family_cache.rs @@ -0,0 +1,52 @@ +use std::sync::Arc; +use std::time::Instant; + +use gpui::{AppContext, Global, ReadGlobal, SharedString}; +use parking_lot::RwLock; + +#[derive(Default)] +struct FontFamilyCacheState { + loaded_at: Option, + font_families: Vec, +} + +/// A cache for the list of font families. +/// +/// Listing the available font families from the text system is expensive, +/// so we do it once and then use the cached values each render. +#[derive(Default)] +pub struct FontFamilyCache { + state: RwLock, +} + +#[derive(Default)] +struct GlobalFontFamilyCache(Arc); + +impl Global for GlobalFontFamilyCache {} + +impl FontFamilyCache { + pub fn init_global(cx: &mut AppContext) { + cx.default_global::(); + } + + pub fn global(cx: &AppContext) -> Arc { + GlobalFontFamilyCache::global(cx).0.clone() + } + + pub fn list_font_families(&self, cx: &AppContext) -> Vec { + if self.state.read().loaded_at.is_some() { + return self.state.read().font_families.clone(); + } + + let mut lock = self.state.write(); + lock.font_families = cx + .text_system() + .all_font_names() + .into_iter() + .map(SharedString::from) + .collect(); + lock.loaded_at = Some(Instant::now()); + + lock.font_families.clone() + } +} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 7bbe7c9885..af38c9efc6 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -8,6 +8,7 @@ mod default_colors; mod default_theme; +mod font_family_cache; mod one_themes; pub mod prelude; mod registry; @@ -21,6 +22,7 @@ use std::sync::Arc; use ::settings::{Settings, SettingsStore}; pub use default_colors::*; pub use default_theme::*; +pub use font_family_cache::*; pub use registry::*; pub use scale::*; pub use schema::*; @@ -82,6 +84,7 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) { } ThemeSettings::register(cx); + FontFamilyCache::init_global(cx); let mut prev_buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size; cx.observe_global::(move |cx| {