Detailed changes
@@ -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 <Self::Settings as Settings>::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;
@@ -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 <Self::Settings as Settings>::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;
@@ -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<Instant>,
+ font_families: Vec<SharedString>,
+}
+
+/// 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<FontFamilyCacheState>,
+}
+
+#[derive(Default)]
+struct GlobalFontFamilyCache(Arc<FontFamilyCache>);
+
+impl Global for GlobalFontFamilyCache {}
+
+impl FontFamilyCache {
+ pub fn init_global(cx: &mut AppContext) {
+ cx.default_global::<GlobalFontFamilyCache>();
+ }
+
+ pub fn global(cx: &AppContext) -> Arc<Self> {
+ GlobalFontFamilyCache::global(cx).0.clone()
+ }
+
+ pub fn list_font_families(&self, cx: &AppContext) -> Vec<SharedString> {
+ 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()
+ }
+}
@@ -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::<SettingsStore>(move |cx| {