ui: Track changes to UI font size made via actions with settings (#23265)

Piotr Osiewicz created

Fixes #5380

Closes #5380

Release Notes:

- Font size changes made with actions are now persisted in user settings

Change summary

crates/assistant2/src/message_editor.rs      |   8 +
crates/editor/src/editor.rs                  |   8 -
crates/outline/src/outline.rs                |   2 
crates/repl/src/outputs/plain.rs             |   3 
crates/terminal_view/src/terminal_element.rs |   7 
crates/theme/src/settings.rs                 | 111 +--------------------
crates/theme/src/theme.rs                    |  12 --
crates/ui/src/styles/typography.rs           |   2 
crates/vim/src/vim.rs                        |   6 
crates/zed/src/zed.rs                        |  84 +++++++++++----
crates/zeta/src/completion_diff_element.rs   |   2 
11 files changed, 89 insertions(+), 156 deletions(-)

Detailed changes

crates/assistant2/src/message_editor.rs 🔗

@@ -11,7 +11,7 @@ use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
 use language_model_selector::LanguageModelSelector;
 use rope::Point;
 use settings::Settings;
-use theme::{get_ui_font_size, ThemeSettings};
+use theme::ThemeSettings;
 use ui::{
     prelude::*, ButtonLike, ElevationIndex, KeyBinding, PopoverMenu, PopoverMenuHandle, Switch,
 };
@@ -313,7 +313,11 @@ impl Render for MessageEditor {
                             .anchor(gpui::Corner::BottomLeft)
                             .offset(gpui::Point {
                                 x: px(0.0),
-                                y: (-get_ui_font_size(cx) * 2) - px(4.0),
+                                y: px(-ThemeSettings::clamp_font_size(
+                                    ThemeSettings::get_global(cx).ui_font_size,
+                                )
+                                .0 * 2.0)
+                                    - px(4.0),
                             })
                             .with_handle(self.inline_context_picker_menu_handle.clone()),
                     )

crates/editor/src/editor.rs 🔗

@@ -160,10 +160,7 @@ use std::{
 pub use sum_tree::Bias;
 use sum_tree::TreeMap;
 use text::{BufferId, OffsetUtf16, Rope};
-use theme::{
-    observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
-    ThemeColors, ThemeSettings,
-};
+use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
 use ui::{
     h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
     PopoverMenuHandle, Tooltip,
@@ -1344,7 +1341,6 @@ impl Editor {
                 cx.observe(&display_map, Self::on_display_map_changed),
                 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
                 cx.observe_global::<SettingsStore>(Self::settings_changed),
-                observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
                 cx.observe_window_activation(|editor, cx| {
                     let active = cx.is_window_active();
                     editor.blink_manager.update(cx, |blink_manager, cx| {
@@ -14366,7 +14362,7 @@ impl Render for Editor {
                 font_family: settings.buffer_font.family.clone(),
                 font_features: settings.buffer_font.features.clone(),
                 font_fallbacks: settings.buffer_font.fallbacks.clone(),
-                font_size: settings.buffer_font_size(cx).into(),
+                font_size: settings.buffer_font_size().into(),
                 font_weight: settings.buffer_font.weight,
                 line_height: relative(settings.buffer_line_height.value()),
                 ..Default::default()

crates/outline/src/outline.rs 🔗

@@ -325,7 +325,7 @@ pub fn render_item<T>(
         font_family: settings.buffer_font.family.clone(),
         font_features: settings.buffer_font.features.clone(),
         font_fallbacks: settings.buffer_font.fallbacks.clone(),
-        font_size: settings.buffer_font_size(cx).into(),
+        font_size: settings.buffer_font_size().into(),
         font_weight: settings.buffer_font.weight,
         line_height: relative(1.),
         ..Default::default()

crates/repl/src/outputs/plain.rs 🔗

@@ -60,6 +60,7 @@ const DEFAULT_NUM_COLUMNS: usize = 128;
 pub fn text_style(cx: &mut WindowContext) -> TextStyle {
     let settings = ThemeSettings::get_global(cx).clone();
 
+    let font_size = settings.buffer_font_size().into();
     let font_family = settings.buffer_font.family;
     let font_features = settings.buffer_font.features;
     let font_weight = settings.buffer_font.weight;
@@ -72,7 +73,7 @@ pub fn text_style(cx: &mut WindowContext) -> TextStyle {
         font_features,
         font_weight,
         font_fallbacks,
-        font_size: theme::get_buffer_font_size(cx).into(),
+        font_size,
         font_style: FontStyle::Normal,
         line_height: cx.line_height().into(),
         background_color: Some(theme.colors().terminal_ansi_background),

crates/terminal_view/src/terminal_element.rs 🔗

@@ -551,7 +551,7 @@ impl TerminalElement {
 
     fn rem_size(&self, cx: &WindowContext) -> Option<Pixels> {
         let settings = ThemeSettings::get_global(cx).clone();
-        let buffer_font_size = settings.buffer_font_size(cx);
+        let buffer_font_size = settings.buffer_font_size();
         let rem_size_scale = {
             // Our default UI font size is 14px on a 16px base scale.
             // This means the default UI font size is 0.875rems.
@@ -608,7 +608,7 @@ impl Element for TerminalElement {
                 let hitbox = hitbox.unwrap();
                 let settings = ThemeSettings::get_global(cx).clone();
 
-                let buffer_font_size = settings.buffer_font_size(cx);
+                let buffer_font_size = settings.buffer_font_size();
 
                 let terminal_settings = TerminalSettings::get_global(cx);
 
@@ -635,8 +635,7 @@ impl Element for TerminalElement {
                 let line_height = terminal_settings.line_height.value();
                 let font_size = terminal_settings.font_size;
 
-                let font_size =
-                    font_size.map_or(buffer_font_size, |size| theme::adjusted_font_size(size, cx));
+                let font_size = font_size.unwrap_or(buffer_font_size);
 
                 let theme = cx.theme().clone();
 

crates/theme/src/settings.rs 🔗

@@ -7,7 +7,7 @@ use anyhow::Result;
 use derive_more::{Deref, DerefMut};
 use gpui::{
     px, AppContext, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels,
-    Subscription, ViewContext, WindowContext,
+    WindowContext,
 };
 use refineable::Refineable;
 use schemars::{
@@ -207,16 +207,6 @@ impl SystemAppearance {
     }
 }
 
-#[derive(Default)]
-pub(crate) struct AdjustedBufferFontSize(Pixels);
-
-impl Global for AdjustedBufferFontSize {}
-
-#[derive(Default)]
-pub(crate) struct AdjustedUiFontSize(Pixels);
-
-impl Global for AdjustedUiFontSize {}
-
 /// Represents the selection of a theme, which can be either static or dynamic.
 #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
 #[serde(untagged)]
@@ -440,10 +430,13 @@ impl BufferLineHeight {
 
 impl ThemeSettings {
     /// Returns the [AdjustedBufferFontSize].
-    pub fn buffer_font_size(&self, cx: &AppContext) -> Pixels {
-        cx.try_global::<AdjustedBufferFontSize>()
-            .map_or(self.buffer_font_size, |size| size.0)
-            .max(MIN_FONT_SIZE)
+    pub fn buffer_font_size(&self) -> Pixels {
+        Self::clamp_font_size(self.buffer_font_size)
+    }
+
+    /// Ensures that the font size is within the valid range.
+    pub fn clamp_font_size(size: Pixels) -> Pixels {
+        size.max(MIN_FONT_SIZE)
     }
 
     // TODO: Rename: `line_height` -> `buffer_line_height`
@@ -500,105 +493,19 @@ impl ThemeSettings {
     }
 }
 
-/// Observe changes to the adjusted buffer font size.
-pub fn observe_buffer_font_size_adjustment<V: 'static>(
-    cx: &mut ViewContext<V>,
-    f: impl 'static + Fn(&mut V, &mut ViewContext<V>),
-) -> Subscription {
-    cx.observe_global::<AdjustedBufferFontSize>(f)
-}
-
-/// Sets the adjusted buffer font size.
-pub fn adjusted_font_size(size: Pixels, cx: &AppContext) -> Pixels {
-    if let Some(AdjustedBufferFontSize(adjusted_size)) = cx.try_global::<AdjustedBufferFontSize>() {
-        let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
-        let delta = *adjusted_size - buffer_font_size;
-        size + delta
-    } else {
-        size
-    }
-    .max(MIN_FONT_SIZE)
-}
-
-/// Returns the adjusted buffer font size.
-pub fn get_buffer_font_size(cx: &AppContext) -> Pixels {
-    let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
-    cx.try_global::<AdjustedBufferFontSize>()
-        .map_or(buffer_font_size, |adjusted_size| adjusted_size.0)
-}
-
-/// Adjusts the buffer font size.
-pub fn adjust_buffer_font_size(cx: &mut AppContext, f: fn(&mut Pixels)) {
-    let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
-    let mut adjusted_size = cx
-        .try_global::<AdjustedBufferFontSize>()
-        .map_or(buffer_font_size, |adjusted_size| adjusted_size.0);
-
-    f(&mut adjusted_size);
-    adjusted_size = adjusted_size.max(MIN_FONT_SIZE);
-    cx.set_global(AdjustedBufferFontSize(adjusted_size));
-    cx.refresh();
-}
-
-/// Returns whether the buffer font size has been adjusted.
-pub fn has_adjusted_buffer_font_size(cx: &AppContext) -> bool {
-    cx.has_global::<AdjustedBufferFontSize>()
-}
-
-/// Resets the buffer font size to the default value.
-pub fn reset_buffer_font_size(cx: &mut AppContext) {
-    if cx.has_global::<AdjustedBufferFontSize>() {
-        cx.remove_global::<AdjustedBufferFontSize>();
-        cx.refresh();
-    }
-}
-
 // TODO: Make private, change usages to use `get_ui_font_size` instead.
 #[allow(missing_docs)]
 pub fn setup_ui_font(cx: &mut WindowContext) -> gpui::Font {
     let (ui_font, ui_font_size) = {
         let theme_settings = ThemeSettings::get_global(cx);
         let font = theme_settings.ui_font.clone();
-        (font, get_ui_font_size(cx))
+        (font, theme_settings.ui_font_size)
     };
 
     cx.set_rem_size(ui_font_size);
     ui_font
 }
 
-/// Gets the adjusted UI font size.
-pub fn get_ui_font_size(cx: &AppContext) -> Pixels {
-    let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
-    cx.try_global::<AdjustedUiFontSize>()
-        .map_or(ui_font_size, |adjusted_size| adjusted_size.0)
-}
-
-/// Sets the adjusted UI font size.
-pub fn adjust_ui_font_size(cx: &mut AppContext, f: fn(&mut Pixels)) {
-    let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
-    let mut adjusted_size = cx
-        .try_global::<AdjustedUiFontSize>()
-        .map_or(ui_font_size, |adjusted_size| adjusted_size.0);
-
-    f(&mut adjusted_size);
-    adjusted_size = adjusted_size.max(MIN_FONT_SIZE);
-    cx.set_global(AdjustedUiFontSize(adjusted_size));
-    cx.refresh();
-}
-
-/// Returns whether the UI font size has been adjusted.
-pub fn has_adjusted_ui_font_size(cx: &AppContext) -> bool {
-    cx.has_global::<AdjustedUiFontSize>()
-}
-
-/// Resets the UI font size to the default value.
-pub fn reset_ui_font_size(cx: &mut AppContext) {
-    if cx.has_global::<AdjustedUiFontSize>() {
-        cx.remove_global::<AdjustedUiFontSize>();
-        cx.refresh();
-    }
-}
-
 fn clamp_font_weight(weight: f32) -> FontWeight {
     FontWeight(weight.clamp(100., 950.))
 }

crates/theme/src/theme.rs 🔗

@@ -22,7 +22,7 @@ mod styles;
 use std::path::Path;
 use std::sync::Arc;
 
-use ::settings::{Settings, SettingsStore};
+use ::settings::Settings;
 use anyhow::Result;
 use fs::Fs;
 use gpui::{
@@ -100,16 +100,6 @@ 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| {
-        let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
-        if buffer_font_size != prev_buffer_font_size {
-            prev_buffer_font_size = buffer_font_size;
-            reset_buffer_font_size(cx);
-        }
-    })
-    .detach();
 }
 
 /// Implementing this trait allows accessing the active theme.

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

@@ -82,7 +82,7 @@ pub trait StyledTypography: Styled + Sized {
     /// or other places that text needs to match the user's buffer font size.
     fn text_buffer(self, cx: &WindowContext) -> Self {
         let settings = ThemeSettings::get_global(cx);
-        self.text_size(settings.buffer_font_size(cx))
+        self.text_size(settings.buffer_font_size())
     }
 }
 

crates/vim/src/vim.rs 🔗

@@ -133,7 +133,7 @@ pub fn init(cx: &mut AppContext) {
             };
 
             let theme = ThemeSettings::get_global(cx);
-            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
+            let height = theme.buffer_font_size() * theme.buffer_line_height.value();
 
             let desired_size = if let Some(count) = Vim::take_count(cx) {
                 height * count
@@ -151,11 +151,11 @@ pub fn init(cx: &mut AppContext) {
             };
             let Ok(width) = cx
                 .text_system()
-                .advance(font_id, theme.buffer_font_size(cx), 'm')
+                .advance(font_id, theme.buffer_font_size(), 'm')
             else {
                 return;
             };
-            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
+            let height = theme.buffer_font_size() * theme.buffer_line_height.value();
 
             let (axis, amount) = match action.0 {
                 ResizeIntent::Lengthen => (Axis::Vertical, height),

crates/zed/src/zed.rs 🔗

@@ -38,14 +38,14 @@ use release_channel::{AppCommitSha, ReleaseChannel};
 use rope::Rope;
 use search::project_search::ProjectSearchBar;
 use settings::{
-    initial_project_settings_content, initial_tasks_content, KeymapFile, Settings, SettingsStore,
-    DEFAULT_KEYMAP_PATH, VIM_KEYMAP_PATH,
+    initial_project_settings_content, initial_tasks_content, update_settings_file, KeymapFile,
+    Settings, SettingsStore, DEFAULT_KEYMAP_PATH, VIM_KEYMAP_PATH,
 };
 use std::any::TypeId;
 use std::path::PathBuf;
 use std::{borrow::Cow, ops::Deref, path::Path, sync::Arc};
 use terminal_view::terminal_panel::{self, TerminalPanel};
-use theme::ActiveTheme;
+use theme::{ActiveTheme, ThemeSettings};
 use util::{asset_str, ResultExt};
 use uuid::Uuid;
 use vim_mode_setting::VimModeSetting;
@@ -454,9 +454,6 @@ fn register_actions(
             OpenListener::global(cx).open_urls(vec![action.url.clone()])
         })
         .register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
-        .register_action(move |_, _: &zed_actions::IncreaseBufferFontSize, cx| {
-            theme::adjust_buffer_font_size(cx, |size| *size += px(1.0))
-        })
         .register_action(|workspace, _: &workspace::Open, cx| {
             workspace
                 .client()
@@ -492,29 +489,68 @@ fn register_actions(
             })
             .detach()
         })
-        .register_action(move |_, _: &zed_actions::DecreaseBufferFontSize, cx| {
-            theme::adjust_buffer_font_size(cx, |size| *size -= px(1.0))
-        })
-        .register_action(move |_, _: &zed_actions::ResetBufferFontSize, cx| {
-            theme::reset_buffer_font_size(cx)
-        })
-        .register_action(move |_, _: &zed_actions::IncreaseUiFontSize, cx| {
-            theme::adjust_ui_font_size(cx, |size| *size += px(1.0))
+        .register_action({
+            let fs = app_state.fs.clone();
+            move |_, _: &zed_actions::IncreaseUiFontSize, cx| {
+                update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    let buffer_font_size = ThemeSettings::clamp_font_size(
+                        ThemeSettings::get_global(cx).ui_font_size + px(1.),
+                    );
+
+                    let _ = settings.ui_font_size.insert(buffer_font_size.into());
+                });
+            }
         })
-        .register_action(move |_, _: &zed_actions::DecreaseUiFontSize, cx| {
-            theme::adjust_ui_font_size(cx, |size| *size -= px(1.0))
+        .register_action({
+            let fs = app_state.fs.clone();
+            move |_, _: &zed_actions::DecreaseUiFontSize, cx| {
+                update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    let buffer_font_size = ThemeSettings::clamp_font_size(
+                        ThemeSettings::get_global(cx).ui_font_size - px(1.),
+                    );
+
+                    let _ = settings.ui_font_size.insert(buffer_font_size.into());
+                });
+            }
         })
-        .register_action(move |_, _: &zed_actions::ResetUiFontSize, cx| {
-            theme::reset_ui_font_size(cx)
+        .register_action({
+            let fs = app_state.fs.clone();
+            move |_, _: &zed_actions::ResetUiFontSize, cx| {
+                update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, _| {
+                    let _ = settings.ui_font_size.take();
+                });
+            }
         })
-        .register_action(move |_, _: &zed_actions::IncreaseBufferFontSize, cx| {
-            theme::adjust_buffer_font_size(cx, |size| *size += px(1.0))
+        .register_action({
+            let fs = app_state.fs.clone();
+            move |_, _: &zed_actions::IncreaseBufferFontSize, cx| {
+                update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    let buffer_font_size = ThemeSettings::clamp_font_size(
+                        ThemeSettings::get_global(cx).buffer_font_size() + px(1.),
+                    );
+
+                    let _ = settings.buffer_font_size.insert(buffer_font_size.into());
+                });
+            }
         })
-        .register_action(move |_, _: &zed_actions::DecreaseBufferFontSize, cx| {
-            theme::adjust_buffer_font_size(cx, |size| *size -= px(1.0))
+        .register_action({
+            let fs = app_state.fs.clone();
+            move |_, _: &zed_actions::DecreaseBufferFontSize, cx| {
+                update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, cx| {
+                    let buffer_font_size = ThemeSettings::clamp_font_size(
+                        ThemeSettings::get_global(cx).buffer_font_size() - px(1.),
+                    );
+                    let _ = settings.buffer_font_size.insert(buffer_font_size.into());
+                });
+            }
         })
-        .register_action(move |_, _: &zed_actions::ResetBufferFontSize, cx| {
-            theme::reset_buffer_font_size(cx)
+        .register_action({
+            let fs = app_state.fs.clone();
+            move |_, _: &zed_actions::ResetBufferFontSize, cx| {
+                update_settings_file::<ThemeSettings>(fs.clone(), cx, move |settings, _| {
+                    let _ = settings.buffer_font_size.take();
+                });
+            }
         })
         .register_action(install_cli)
         .register_action(|_, _: &install_cli::RegisterZedScheme, cx| {

crates/zeta/src/completion_diff_element.rs 🔗

@@ -69,7 +69,7 @@ impl CompletionDiffElement {
         let settings = ThemeSettings::get_global(cx).clone();
         let text_style = TextStyle {
             color: cx.theme().colors().editor_foreground,
-            font_size: settings.buffer_font_size(cx).into(),
+            font_size: settings.buffer_font_size().into(),
             font_family: settings.buffer_font.family,
             font_features: settings.buffer_font.features,
             font_fallbacks: settings.buffer_font.fallbacks,