Allow UI font weight to be assigned in settings (#12333)

Nathan Sobo created

Release Notes:

- Added the ability to configure the weight of your UI font in standard
CSS weight units from 0 to 900.

Change summary

assets/settings/default.json                                      |  4 
crates/assistant/src/completion_provider/anthropic.rs             |  4 
crates/assistant/src/completion_provider/open_ai.rs               |  4 
crates/assistant2/src/ui/composer.rs                              |  4 
crates/collab_ui/src/chat_panel/message_editor.rs                 |  2 
crates/collab_ui/src/collab_panel.rs                              | 10 
crates/collab_ui/src/notifications/incoming_call_notification.rs  |  7 
crates/collab_ui/src/notifications/project_shared_notification.rs |  7 
crates/editor/src/editor.rs                                       |  4 
crates/extensions_ui/src/extensions_ui.rs                         |  4 
crates/gpui/src/text_system.rs                                    |  4 
crates/project_panel/src/project_panel.rs                         |  4 
crates/search/src/project_search.rs                               | 10 
crates/semantic_index/src/project_index_debug_view.rs             |  4 
crates/terminal/src/terminal_settings.rs                          |  5 
crates/terminal_view/src/terminal_element.rs                      |  4 
crates/theme/src/settings.rs                                      | 17 
crates/ui/src/components/tooltip.rs                               |  4 
crates/ui/src/styles/typography.rs                                |  4 
crates/ui_text_field/src/ui_text_field.rs                         |  2 
crates/workspace/src/pane.rs                                      |  4 
crates/workspace/src/workspace.rs                                 |  7 
22 files changed, 67 insertions(+), 52 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -37,6 +37,8 @@
   },
   // The default font size for text in the editor
   "buffer_font_size": 15,
+  // The weight of the editor font in standard CSS units from 100 to 900.
+  "buffer_font_weight": 400,
   // Set the buffer's line height.
   // May take 3 values:
   //  1. Use a line height that's comfortable for reading (1.618)
@@ -55,6 +57,8 @@
     // Disable ligatures:
     "calt": false
   },
+  // The weight of the UI font in standard CSS units from 100 to 900.
+  "ui_font_weight": 400,
   // The default font size for text in the UI
   "ui_font_size": 16,
   // The factor to grow the active pane by. Defaults to 1.0

crates/assistant/src/completion_provider/anthropic.rs 🔗

@@ -7,7 +7,7 @@ use anthropic::{stream_completion, Request, RequestMessage, Role as AnthropicRol
 use anyhow::{anyhow, Result};
 use editor::{Editor, EditorElement, EditorStyle};
 use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
-use gpui::{AnyView, AppContext, FontStyle, FontWeight, Task, TextStyle, View, WhiteSpace};
+use gpui::{AnyView, AppContext, FontStyle, Task, TextStyle, View, WhiteSpace};
 use http::HttpClient;
 use settings::Settings;
 use std::time::Duration;
@@ -261,7 +261,7 @@ impl AuthenticationPrompt {
             font_family: settings.ui_font.family.clone(),
             font_features: settings.ui_font.features.clone(),
             font_size: rems(0.875).into(),
-            font_weight: FontWeight::NORMAL,
+            font_weight: settings.ui_font.weight,
             font_style: FontStyle::Normal,
             line_height: relative(1.3),
             background_color: None,

crates/assistant/src/completion_provider/open_ai.rs 🔗

@@ -5,7 +5,7 @@ use crate::{
 use anyhow::{anyhow, Result};
 use editor::{Editor, EditorElement, EditorStyle};
 use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
-use gpui::{AnyView, AppContext, FontStyle, FontWeight, Task, TextStyle, View, WhiteSpace};
+use gpui::{AnyView, AppContext, FontStyle, Task, TextStyle, View, WhiteSpace};
 use http::HttpClient;
 use open_ai::{stream_completion, Request, RequestMessage, Role as OpenAiRole};
 use settings::Settings;
@@ -273,7 +273,7 @@ impl AuthenticationPrompt {
             font_family: settings.ui_font.family.clone(),
             font_features: settings.ui_font.features.clone(),
             font_size: rems(0.875).into(),
-            font_weight: FontWeight::NORMAL,
+            font_weight: settings.ui_font.weight,
             font_style: FontStyle::Normal,
             line_height: relative(1.3),
             background_color: None,

crates/assistant2/src/ui/composer.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     AssistantChat, CompletionProvider,
 };
 use editor::{Editor, EditorElement, EditorStyle};
-use gpui::{AnyElement, FontStyle, FontWeight, ReadGlobal, TextStyle, View, WeakView, WhiteSpace};
+use gpui::{AnyElement, FontStyle, ReadGlobal, TextStyle, View, WeakView, WhiteSpace};
 use settings::Settings;
 use theme::ThemeSettings;
 use ui::{popover_menu, prelude::*, ButtonLike, ContextMenu, Divider, TextSize, Tooltip};
@@ -78,7 +78,7 @@ impl RenderOnce for Composer {
                                     font_family: settings.buffer_font.family.clone(),
                                     font_features: settings.buffer_font.features.clone(),
                                     font_size: font_size.into(),
-                                    font_weight: FontWeight::NORMAL,
+                                    font_weight: settings.buffer_font.weight,
                                     font_style: FontStyle::Normal,
                                     line_height: line_height.into(),
                                     background_color: None,

crates/collab_ui/src/chat_panel/message_editor.rs 🔗

@@ -524,7 +524,7 @@ impl Render for MessageEditor {
             font_family: settings.ui_font.family.clone(),
             font_features: settings.ui_font.features.clone(),
             font_size: TextSize::Small.rems(cx).into(),
-            font_weight: FontWeight::NORMAL,
+            font_weight: settings.ui_font.weight,
             font_style: FontStyle::Normal,
             line_height: relative(1.3),
             background_color: None,

crates/collab_ui/src/collab_panel.rs 🔗

@@ -16,10 +16,10 @@ use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
     actions, anchored, canvas, deferred, div, fill, list, point, prelude::*, px, AnyElement,
     AppContext, AsyncWindowContext, Bounds, ClickEvent, ClipboardItem, DismissEvent, Div,
-    EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, InteractiveElement,
-    IntoElement, ListOffset, ListState, Model, MouseDownEvent, ParentElement, Pixels, Point,
-    PromptLevel, Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext,
-    VisualContext, WeakView, WhiteSpace,
+    EventEmitter, FocusHandle, FocusableView, FontStyle, InteractiveElement, IntoElement,
+    ListOffset, ListState, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel,
+    Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext,
+    WeakView, WhiteSpace,
 };
 use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
 use project::{Fs, Project};
@@ -2190,7 +2190,7 @@ impl CollabPanel {
             font_family: settings.ui_font.family.clone(),
             font_features: settings.ui_font.features.clone(),
             font_size: rems(0.875).into(),
-            font_weight: FontWeight::NORMAL,
+            font_weight: settings.ui_font.weight,
             font_style: FontStyle::Normal,
             line_height: relative(1.3),
             background_color: None,

crates/collab_ui/src/notifications/incoming_call_notification.rs 🔗

@@ -116,15 +116,12 @@ impl Render for IncomingCallNotification {
         // TODO: Is there a better place for us to initialize the font?
         let (ui_font, ui_font_size) = {
             let theme_settings = ThemeSettings::get_global(cx);
-            (
-                theme_settings.ui_font.family.clone(),
-                theme_settings.ui_font_size,
-            )
+            (theme_settings.ui_font.clone(), theme_settings.ui_font_size)
         };
 
         cx.set_rem_size(ui_font_size);
 
-        div().size_full().font_family(ui_font).child(
+        div().size_full().font(ui_font).child(
             CollabNotification::new(
                 self.state.call.calling_user.avatar_uri.clone(),
                 Button::new("accept", "Accept").on_click({

crates/collab_ui/src/notifications/project_shared_notification.rs 🔗

@@ -121,15 +121,12 @@ impl Render for ProjectSharedNotification {
         // TODO: Is there a better place for us to initialize the font?
         let (ui_font, ui_font_size) = {
             let theme_settings = ThemeSettings::get_global(cx);
-            (
-                theme_settings.ui_font.family.clone(),
-                theme_settings.ui_font_size,
-            )
+            (theme_settings.ui_font.clone(), theme_settings.ui_font_size)
         };
 
         cx.set_rem_size(ui_font_size);
 
-        div().size_full().font_family(ui_font).child(
+        div().size_full().font(ui_font).child(
             CollabNotification::new(
                 self.owner.avatar_uri.clone(),
                 Button::new("open", "Open").on_click(cx.listener(move |this, _event, cx| {

crates/editor/src/editor.rs 🔗

@@ -11466,7 +11466,7 @@ impl Render for Editor {
                 font_family: settings.ui_font.family.clone(),
                 font_features: settings.ui_font.features.clone(),
                 font_size: rems(0.875).into(),
-                font_weight: FontWeight::NORMAL,
+                font_weight: settings.ui_font.weight,
                 font_style: FontStyle::Normal,
                 line_height: relative(settings.buffer_line_height.value()),
                 background_color: None,
@@ -11479,7 +11479,7 @@ impl Render for Editor {
                 font_family: settings.buffer_font.family.clone(),
                 font_features: settings.buffer_font.features.clone(),
                 font_size: settings.buffer_font_size(cx).into(),
-                font_weight: FontWeight::NORMAL,
+                font_weight: settings.buffer_font.weight,
                 font_style: FontStyle::Normal,
                 line_height: relative(settings.buffer_line_height.value()),
                 background_color: None,

crates/extensions_ui/src/extensions_ui.rs 🔗

@@ -13,7 +13,7 @@ use extension::{ExtensionManifest, ExtensionOperation, ExtensionStore};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
     actions, canvas, uniform_list, AnyElement, AppContext, EventEmitter, FocusableView, FontStyle,
-    FontWeight, InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
+    InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
     UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
 };
 use release_channel::ReleaseChannel;
@@ -743,7 +743,7 @@ impl ExtensionsPage {
             font_family: settings.ui_font.family.clone(),
             font_features: settings.ui_font.features.clone(),
             font_size: rems(0.875).into(),
-            font_weight: FontWeight::NORMAL,
+            font_weight: settings.ui_font.weight,
             font_style: FontStyle::Normal,
             line_height: relative(1.3),
             background_color: None,

crates/gpui/src/text_system.rs 🔗

@@ -7,6 +7,8 @@ pub use font_features::*;
 pub use line::*;
 pub use line_layout::*;
 pub use line_wrapper::*;
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
 
 use crate::{
     px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size,
@@ -554,7 +556,7 @@ impl DerefMut for LineWrapperHandle {
 
 /// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0,
 /// with 400.0 as normal.
-#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Deserialize, Serialize, JsonSchema)]
 pub struct FontWeight(pub f32);
 
 impl Default for FontWeight {

crates/project_panel/src/project_panel.rs 🔗

@@ -1961,9 +1961,9 @@ impl Render for ProjectPanel {
 impl Render for DraggedProjectEntryView {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let settings = ProjectPanelSettings::get_global(cx);
-        let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
+        let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
         h_flex()
-            .font_family(ui_font)
+            .font(ui_font)
             .bg(cx.theme().colors().background)
             .w(self.width)
             .child(

crates/search/src/project_search.rs 🔗

@@ -13,10 +13,10 @@ use editor::{
 };
 use gpui::{
     actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId,
-    EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Global, Hsla,
-    InteractiveElement, IntoElement, Model, ModelContext, ParentElement, Point, Render,
-    SharedString, Styled, Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext,
-    VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext,
+    EventEmitter, FocusHandle, FocusableView, FontStyle, Global, Hsla, InteractiveElement,
+    IntoElement, Model, ModelContext, ParentElement, Point, Render, SharedString, Styled,
+    Subscription, Task, TextStyle, UpdateGlobal, View, ViewContext, VisualContext, WeakModel,
+    WeakView, WhiteSpace, WindowContext,
 };
 use menu::Confirm;
 use project::{search::SearchQuery, search_history::SearchHistoryCursor, Project, ProjectPath};
@@ -1311,7 +1311,7 @@ impl ProjectSearchBar {
             font_family: settings.buffer_font.family.clone(),
             font_features: settings.buffer_font.features.clone(),
             font_size: rems(0.875).into(),
-            font_weight: FontWeight::NORMAL,
+            font_weight: settings.buffer_font.weight,
             font_style: FontStyle::Normal,
             line_height: relative(1.3),
             background_color: None,

crates/semantic_index/src/project_index_debug_view.rs 🔗

@@ -131,7 +131,7 @@ impl ProjectIndexDebugView {
     }
 
     fn render_chunk(&mut self, ix: usize, cx: &mut ViewContext<Self>) -> AnyElement {
-        let buffer_font = ThemeSettings::get_global(cx).buffer_font.family.clone();
+        let buffer_font = ThemeSettings::get_global(cx).buffer_font.clone();
         let Some(state) = &self.selected_path else {
             return div().into_any();
         };
@@ -142,7 +142,7 @@ impl ProjectIndexDebugView {
         div()
             .text_ui(cx)
             .w_full()
-            .font_family(buffer_font)
+            .font(buffer_font)
             .child(
                 h_flex()
                     .justify_between()

crates/terminal/src/terminal_settings.rs 🔗

@@ -1,5 +1,5 @@
 use collections::HashMap;
-use gpui::{px, AbsoluteLength, AppContext, FontFeatures, Pixels};
+use gpui::{px, AbsoluteLength, AppContext, FontFeatures, FontWeight, Pixels};
 use schemars::{
     gen::SchemaGenerator,
     schema::{InstanceType, RootSchema, Schema, SchemaObject},
@@ -31,6 +31,7 @@ pub struct TerminalSettings {
     pub font_family: Option<String>,
     pub line_height: TerminalLineHeight,
     pub font_features: Option<FontFeatures>,
+    pub font_weight: Option<FontWeight>,
     pub env: HashMap<String, String>,
     pub blinking: TerminalBlink,
     pub alternate_scroll: AlternateScroll,
@@ -114,6 +115,8 @@ pub struct TerminalSettingsContent {
     /// Default: comfortable
     pub line_height: Option<TerminalLineHeight>,
     pub font_features: Option<FontFeatures>,
+    /// Sets the terminal's font weight in CSS weight units 0-900.
+    pub font_weight: Option<f32>,
     /// Any key-value pairs added to this list will be added to the terminal's
     /// environment. Use `:` to separate multiple values.
     ///

crates/terminal_view/src/terminal_element.rs 🔗

@@ -592,6 +592,8 @@ impl Element for TerminalElement {
                     .clone()
                     .unwrap_or(settings.buffer_font.features.clone());
 
+                let font_weight = terminal_settings.font_weight.unwrap_or_default();
+
                 let line_height = terminal_settings.line_height.value();
                 let font_size = terminal_settings.font_size;
 
@@ -617,6 +619,7 @@ impl Element for TerminalElement {
                 let text_style = TextStyle {
                     font_family,
                     font_features,
+                    font_weight,
                     font_size: font_size.into(),
                     font_style: FontStyle::Normal,
                     line_height: line_height.into(),
@@ -626,7 +629,6 @@ impl Element for TerminalElement {
                     underline: None,
                     strikethrough: None,
                     color: theme.colors().text,
-                    font_weight: FontWeight::NORMAL,
                 };
 
                 let text_system = cx.text_system();

crates/theme/src/settings.rs 🔗

@@ -227,12 +227,18 @@ pub struct ThemeSettingsContent {
     /// The OpenType features to enable for text in the UI.
     #[serde(default)]
     pub ui_font_features: Option<FontFeatures>,
+    /// The weight of the UI font in CSS units from 100 to 900.
+    #[serde(default)]
+    pub ui_font_weight: Option<f32>,
     /// The name of a font to use for rendering in text buffers.
     #[serde(default)]
     pub buffer_font_family: Option<String>,
     /// The default font size for rendering in text buffers.
     #[serde(default)]
     pub buffer_font_size: Option<f32>,
+    /// The weight of the editor font in CSS units from 100 to 900.
+    #[serde(default)]
+    pub buffer_font_weight: Option<f32>,
     /// The buffer's line height.
     #[serde(default)]
     pub buffer_line_height: Option<BufferLineHeight>,
@@ -386,13 +392,13 @@ impl settings::Settings for ThemeSettings {
             ui_font: Font {
                 family: defaults.ui_font_family.clone().unwrap().into(),
                 features: defaults.ui_font_features.clone().unwrap(),
-                weight: Default::default(),
+                weight: defaults.ui_font_weight.map(FontWeight).unwrap(),
                 style: Default::default(),
             },
             buffer_font: Font {
                 family: defaults.buffer_font_family.clone().unwrap().into(),
                 features: defaults.buffer_font_features.clone().unwrap(),
-                weight: FontWeight::default(),
+                weight: defaults.buffer_font_weight.map(FontWeight).unwrap(),
                 style: FontStyle::default(),
             },
             buffer_font_size: defaults.buffer_font_size.unwrap().into(),
@@ -418,12 +424,19 @@ impl settings::Settings for ThemeSettings {
                 this.buffer_font.features = value;
             }
 
+            if let Some(value) = value.buffer_font_weight {
+                this.buffer_font.weight = FontWeight(value);
+            }
+
             if let Some(value) = value.ui_font_family.clone() {
                 this.ui_font.family = value.into();
             }
             if let Some(value) = value.ui_font_features.clone() {
                 this.ui_font.features = value;
             }
+            if let Some(value) = value.ui_font_weight {
+                this.ui_font.weight = FontWeight(value);
+            }
 
             if let Some(value) = &value.theme {
                 this.theme_selection = Some(value.clone());

crates/ui/src/components/tooltip.rs 🔗

@@ -89,13 +89,13 @@ pub fn tooltip_container<V>(
     cx: &mut ViewContext<V>,
     f: impl FnOnce(Div, &mut ViewContext<V>) -> Div,
 ) -> impl IntoElement {
-    let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
+    let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
 
     // padding to avoid tooltip appearing right below the mouse cursor
     div().pl_2().pt_2p5().child(
         v_flex()
             .elevation_2(cx)
-            .font_family(ui_font)
+            .font(ui_font)
             .text_ui(cx)
             .text_color(cx.theme().colors().text)
             .py_1()

crates/ui/src/styles/typography.rs 🔗

@@ -169,10 +169,10 @@ pub struct Headline {
 
 impl RenderOnce for Headline {
     fn render(self, cx: &mut WindowContext) -> impl IntoElement {
-        let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
+        let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
 
         div()
-            .font_family(ui_font)
+            .font(ui_font)
             .line_height(self.size.line_height())
             .text_size(self.size.size())
             .text_color(cx.theme().colors().text)

crates/ui_text_field/src/ui_text_field.rs 🔗

@@ -125,7 +125,7 @@ impl Render for TextField {
             font_family: settings.buffer_font.family.clone(),
             font_features: settings.buffer_font.features.clone(),
             font_size: rems(0.875).into(),
-            font_weight: FontWeight::NORMAL,
+            font_weight: settings.buffer_font.weight,
             font_style: FontStyle::Normal,
             line_height: relative(1.2),
             color: style.text_color,

crates/workspace/src/pane.rs 🔗

@@ -3027,7 +3027,7 @@ mod tests {
 
 impl Render for DraggedTab {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
+        let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
         let label = self.item.tab_content(
             TabContentParams {
                 detail: Some(self.detail),
@@ -3040,6 +3040,6 @@ impl Render for DraggedTab {
             .selected(self.is_active)
             .child(label)
             .render(cx)
-            .font_family(ui_font)
+            .font(ui_font)
     }
 }

crates/workspace/src/workspace.rs 🔗

@@ -4123,10 +4123,7 @@ impl Render for Workspace {
         };
         let (ui_font, ui_font_size) = {
             let theme_settings = ThemeSettings::get_global(cx);
-            (
-                theme_settings.ui_font.family.clone(),
-                theme_settings.ui_font_size,
-            )
+            (theme_settings.ui_font.clone(), theme_settings.ui_font_size)
         };
 
         let theme = cx.theme().clone();
@@ -4139,7 +4136,7 @@ impl Render for Workspace {
             .size_full()
             .flex()
             .flex_col()
-            .font_family(ui_font)
+            .font(ui_font)
             .gap_0()
             .justify_start()
             .items_start()