Break typography styles out of `StyledExt` (#11013)

Nate Butler created

- Centralizes typography-related UI styles and methods in
`styles/typography.rs`
- Breaks the typography-related styles out of `StyledExt`. This means we
add a `StyledTypography` trait – this should more or less be an
invisible change as we publish it in the prelude.
- adds the ability to easily grab the UI or Buffer font sizes
(`ui_font_size`, `buffer_font_size`) with `TextSize::UI`,
`TextSize::Editor`

Release Notes:

- N/A

Change summary

crates/breadcrumbs/src/breadcrumbs.rs                     |  2 
crates/collab_ui/src/chat_panel.rs                        | 12 
crates/collab_ui/src/chat_panel/message_editor.rs         |  4 
crates/collab_ui/src/notifications/collab_notification.rs |  2 
crates/outline/src/outline.rs                             |  2 
crates/ui/src/components/keybinding.rs                    |  2 
crates/ui/src/components/label/label_like.rs              |  8 
crates/ui/src/components/tooltip.rs                       |  2 
crates/ui/src/prelude.rs                                  |  2 
crates/ui/src/styled_ext.rs                               | 64 ------
crates/ui/src/styles/typography.rs                        | 84 ++++++++
11 files changed, 100 insertions(+), 84 deletions(-)

Detailed changes

crates/breadcrumbs/src/breadcrumbs.rs 🔗

@@ -33,7 +33,7 @@ impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 impl Render for Breadcrumbs {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         const MAX_SEGMENTS: usize = 12;
-        let element = h_flex().text_ui();
+        let element = h_flex().text_ui(cx);
         let Some(active_item) = self.active_item.as_ref() else {
             return element;
         };

crates/collab_ui/src/chat_panel.rs 🔗

@@ -315,7 +315,7 @@ impl ChatPanel {
             None => {
                 return div().child(
                     h_flex()
-                        .text_ui_xs()
+                        .text_ui_xs(cx)
                         .my_0p5()
                         .px_0p5()
                         .gap_x_1()
@@ -350,7 +350,7 @@ impl ChatPanel {
         div().child(
             h_flex()
                 .id(message_element_id)
-                .text_ui_xs()
+                .text_ui_xs(cx)
                 .my_0p5()
                 .px_0p5()
                 .gap_x_1()
@@ -495,7 +495,7 @@ impl ChatPanel {
                         |this| {
                             this.child(
                                 h_flex()
-                                    .text_ui_sm()
+                                    .text_ui_sm(cx)
                                     .child(
                                         div().absolute().child(
                                             Avatar::new(message.sender.avatar_uri.clone())
@@ -539,7 +539,7 @@ impl ChatPanel {
                         el.child(
                             v_flex()
                                 .w_full()
-                                .text_ui_sm()
+                                .text_ui_sm(cx)
                                 .id(element_id)
                                 .child(text.element("body".into(), cx)),
                         )
@@ -562,7 +562,7 @@ impl ChatPanel {
                                 div()
                                     .px_1()
                                     .rounded_md()
-                                    .text_ui_xs()
+                                    .text_ui_xs(cx)
                                     .bg(cx.theme().colors().background)
                                     .child("New messages"),
                             )
@@ -1003,7 +1003,7 @@ impl Render for ChatPanel {
                 el.child(
                     h_flex()
                         .px_2()
-                        .text_ui_xs()
+                        .text_ui_xs(cx)
                         .justify_between()
                         .border_t_1()
                         .border_color(cx.theme().colors().border)

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

@@ -18,7 +18,7 @@ use project::{search::SearchQuery, Completion};
 use settings::Settings;
 use std::{ops::Range, sync::Arc, time::Duration};
 use theme::ThemeSettings;
-use ui::{prelude::*, UiTextSize};
+use ui::{prelude::*, TextSize};
 
 use crate::panel_settings::MessageEditorSettings;
 
@@ -523,7 +523,7 @@ impl Render for MessageEditor {
             },
             font_family: settings.ui_font.family.clone(),
             font_features: settings.ui_font.features,
-            font_size: UiTextSize::Small.rems().into(),
+            font_size: TextSize::Small.rems(cx).into(),
             font_weight: FontWeight::NORMAL,
             font_style: FontStyle::Normal,
             line_height: relative(1.3),

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

@@ -34,7 +34,7 @@ impl ParentElement for CollabNotification {
 impl RenderOnce for CollabNotification {
     fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         h_flex()
-            .text_ui()
+            .text_ui(cx)
             .justify_between()
             .size_full()
             .overflow_hidden()

crates/outline/src/outline.rs 🔗

@@ -306,7 +306,7 @@ impl PickerDelegate for OutlineViewDelegate {
                 .selected(selected)
                 .child(
                     div()
-                        .text_ui()
+                        .text_ui(cx)
                         .pl(rems(outline_item.depth as f32))
                         .child(styled_text),
                 ),

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

@@ -160,7 +160,7 @@ impl RenderOnce for Key {
                 }
             })
             .h(rems_from_px(14.))
-            .text_ui()
+            .text_ui(cx)
             .line_height(relative(1.))
             .text_color(cx.theme().colors().text_muted)
             .child(self.key.clone())

crates/ui/src/components/label/label_like.rs 🔗

@@ -108,10 +108,10 @@ impl RenderOnce for LabelLike {
                 )
             })
             .map(|this| match self.size {
-                LabelSize::Large => this.text_ui_lg(),
-                LabelSize::Default => this.text_ui(),
-                LabelSize::Small => this.text_ui_sm(),
-                LabelSize::XSmall => this.text_ui_xs(),
+                LabelSize::Large => this.text_ui_lg(cx),
+                LabelSize::Default => this.text_ui(cx),
+                LabelSize::Small => this.text_ui_sm(cx),
+                LabelSize::XSmall => this.text_ui_xs(cx),
             })
             .when(self.line_height_style == LineHeightStyle::UiLabel, |this| {
                 this.line_height(relative(1.))

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

@@ -96,7 +96,7 @@ pub fn tooltip_container<V>(
         v_flex()
             .elevation_2(cx)
             .font_family(ui_font)
-            .text_ui()
+            .text_ui(cx)
             .text_color(cx.theme().colors().text)
             .py_1()
             .px_2()

crates/ui/src/prelude.rs 🔗

@@ -11,7 +11,7 @@ pub use crate::clickable::*;
 pub use crate::disableable::*;
 pub use crate::fixed::*;
 pub use crate::selectable::*;
-pub use crate::styles::{rems_from_px, vh, vw, PlatformStyle};
+pub use crate::styles::{rems_from_px, vh, vw, PlatformStyle, StyledTypography};
 pub use crate::visible_on_hover::*;
 pub use crate::{h_flex, v_flex};
 pub use crate::{Button, ButtonSize, ButtonStyle, IconButton, SelectableButton};

crates/ui/src/styled_ext.rs 🔗

@@ -1,9 +1,7 @@
 use gpui::{hsla, px, Styled, WindowContext};
-use settings::Settings;
-use theme::ThemeSettings;
 
 use crate::prelude::*;
-use crate::{ElevationIndex, UiTextSize};
+use crate::ElevationIndex;
 
 fn elevated<E: Styled>(this: E, cx: &mut WindowContext, index: ElevationIndex) -> E {
     this.bg(cx.theme().colors().elevated_surface_background)
@@ -29,66 +27,6 @@ pub trait StyledExt: Styled + Sized {
         self.flex().flex_col()
     }
 
-    /// Sets the text size using a [`UiTextSize`].
-    fn text_ui_size(self, size: UiTextSize) -> Self {
-        self.text_size(size.rems())
-    }
-
-    /// The large size for UI text.
-    ///
-    /// `1rem` or `16px` at the default scale of `1rem` = `16px`.
-    ///
-    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
-    ///
-    /// Use `text_ui` for regular-sized text.
-    fn text_ui_lg(self) -> Self {
-        self.text_size(UiTextSize::Large.rems())
-    }
-
-    /// The default size for UI text.
-    ///
-    /// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
-    ///
-    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
-    ///
-    /// Use `text_ui_sm` for smaller text.
-    fn text_ui(self) -> Self {
-        self.text_size(UiTextSize::default().rems())
-    }
-
-    /// The small size for UI text.
-    ///
-    /// `0.75rem` or `12px` at the default scale of `1rem` = `16px`.
-    ///
-    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
-    ///
-    /// Use `text_ui` for regular-sized text.
-    fn text_ui_sm(self) -> Self {
-        self.text_size(UiTextSize::Small.rems())
-    }
-
-    /// The extra small size for UI text.
-    ///
-    /// `0.625rem` or `10px` at the default scale of `1rem` = `16px`.
-    ///
-    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
-    ///
-    /// Use `text_ui` for regular-sized text.
-    fn text_ui_xs(self) -> Self {
-        self.text_size(UiTextSize::XSmall.rems())
-    }
-
-    /// The font size for buffer text.
-    ///
-    /// Retrieves the default font size, or the user's custom font size if set.
-    ///
-    /// This should only be used for text that is displayed in a buffer,
-    /// or other places that text needs to match the user's buffer font size.
-    fn text_buffer(self, cx: &mut WindowContext) -> Self {
-        let settings = ThemeSettings::get_global(cx);
-        self.text_size(settings.buffer_font_size(cx))
-    }
-
     /// The [`Surface`](ElevationIndex::Surface) elevation level, located above the app background, is the standard level for all elements
     ///
     /// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`

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

@@ -6,8 +6,73 @@ use theme::{ActiveTheme, ThemeSettings};
 
 use crate::rems_from_px;
 
+/// Extends [`gpui::Styled`] with typography-related styling methods.
+pub trait StyledTypography: Styled + Sized {
+    /// Sets the text size using a [`UiTextSize`].
+    fn text_ui_size(self, size: TextSize, cx: &WindowContext) -> Self {
+        self.text_size(size.rems(cx))
+    }
+
+    /// The large size for UI text.
+    ///
+    /// `1rem` or `16px` at the default scale of `1rem` = `16px`.
+    ///
+    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
+    ///
+    /// Use `text_ui` for regular-sized text.
+    fn text_ui_lg(self, cx: &WindowContext) -> Self {
+        self.text_size(TextSize::Large.rems(cx))
+    }
+
+    /// The default size for UI text.
+    ///
+    /// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
+    ///
+    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
+    ///
+    /// Use `text_ui_sm` for smaller text.
+    fn text_ui(self, cx: &WindowContext) -> Self {
+        self.text_size(TextSize::default().rems(cx))
+    }
+
+    /// The small size for UI text.
+    ///
+    /// `0.75rem` or `12px` at the default scale of `1rem` = `16px`.
+    ///
+    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
+    ///
+    /// Use `text_ui` for regular-sized text.
+    fn text_ui_sm(self, cx: &WindowContext) -> Self {
+        self.text_size(TextSize::Small.rems(cx))
+    }
+
+    /// The extra small size for UI text.
+    ///
+    /// `0.625rem` or `10px` at the default scale of `1rem` = `16px`.
+    ///
+    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
+    ///
+    /// Use `text_ui` for regular-sized text.
+    fn text_ui_xs(self, cx: &WindowContext) -> Self {
+        self.text_size(TextSize::XSmall.rems(cx))
+    }
+
+    /// The font size for buffer text.
+    ///
+    /// Retrieves the default font size, or the user's custom font size if set.
+    ///
+    /// This should only be used for text that is displayed in a buffer,
+    /// or other places that text needs to match the user's buffer font size.
+    fn text_buffer(self, cx: &mut WindowContext) -> Self {
+        let settings = ThemeSettings::get_global(cx);
+        self.text_size(settings.buffer_font_size(cx))
+    }
+}
+
+impl<E: Styled> StyledTypography for E {}
+
 #[derive(Debug, Default, Clone)]
-pub enum UiTextSize {
+pub enum TextSize {
     /// The default size for UI text.
     ///
     /// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
@@ -35,15 +100,28 @@ pub enum UiTextSize {
     ///
     /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
     XSmall,
+
+    /// The `ui_font_size` set by the user.
+    UI,
+    /// The `buffer_font_size` set by the user.
+    Editor,
+    // TODO: The terminal settings will need to be passed to
+    // ThemeSettings before we can enable this.
+    //// The `terminal.font_size` set by the user.
+    // Terminal,
 }
 
-impl UiTextSize {
-    pub fn rems(self) -> Rems {
+impl TextSize {
+    pub fn rems(self, cx: &WindowContext) -> Rems {
+        let theme_settings = ThemeSettings::get_global(cx);
+
         match self {
             Self::Large => rems_from_px(16.),
             Self::Default => rems_from_px(14.),
             Self::Small => rems_from_px(12.),
             Self::XSmall => rems_from_px(10.),
+            Self::UI => rems_from_px(theme_settings.ui_font_size.into()),
+            Self::Editor => rems_from_px(theme_settings.buffer_font_size.into()),
         }
     }
 }