typography.rs

  1use gpui::{
  2    div, rems, AppContext, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled,
  3    WindowContext,
  4};
  5use settings::Settings;
  6use theme::{ActiveTheme, ThemeSettings};
  7
  8use crate::{rems_from_px, Color};
  9
 10/// Extends [`gpui::Styled`] with typography-related styling methods.
 11pub trait StyledTypography: Styled + Sized {
 12    /// Sets the font family to the buffer font.
 13    fn font_buffer(self, cx: &WindowContext) -> Self {
 14        let settings = ThemeSettings::get_global(cx);
 15        let buffer_font_family = settings.buffer_font.family.clone();
 16
 17        self.font_family(buffer_font_family)
 18    }
 19
 20    /// Sets the font family to the UI font.
 21    fn font_ui(self, cx: &WindowContext) -> Self {
 22        let settings = ThemeSettings::get_global(cx);
 23        let ui_font_family = settings.ui_font.family.clone();
 24
 25        self.font_family(ui_font_family)
 26    }
 27
 28    /// Sets the text size using a [`UiTextSize`].
 29    fn text_ui_size(self, size: TextSize, cx: &WindowContext) -> Self {
 30        self.text_size(size.rems(cx))
 31    }
 32
 33    /// The large size for UI text.
 34    ///
 35    /// `1rem` or `16px` at the default scale of `1rem` = `16px`.
 36    ///
 37    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
 38    ///
 39    /// Use `text_ui` for regular-sized text.
 40    fn text_ui_lg(self, cx: &WindowContext) -> Self {
 41        self.text_size(TextSize::Large.rems(cx))
 42    }
 43
 44    /// The default size for UI text.
 45    ///
 46    /// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
 47    ///
 48    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
 49    ///
 50    /// Use `text_ui_sm` for smaller text.
 51    fn text_ui(self, cx: &WindowContext) -> Self {
 52        self.text_size(TextSize::default().rems(cx))
 53    }
 54
 55    /// The small size for UI text.
 56    ///
 57    /// `0.75rem` or `12px` at the default scale of `1rem` = `16px`.
 58    ///
 59    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
 60    ///
 61    /// Use `text_ui` for regular-sized text.
 62    fn text_ui_sm(self, cx: &WindowContext) -> Self {
 63        self.text_size(TextSize::Small.rems(cx))
 64    }
 65
 66    /// The extra small size for UI text.
 67    ///
 68    /// `0.625rem` or `10px` at the default scale of `1rem` = `16px`.
 69    ///
 70    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
 71    ///
 72    /// Use `text_ui` for regular-sized text.
 73    fn text_ui_xs(self, cx: &WindowContext) -> Self {
 74        self.text_size(TextSize::XSmall.rems(cx))
 75    }
 76
 77    /// The font size for buffer text.
 78    ///
 79    /// Retrieves the default font size, or the user's custom font size if set.
 80    ///
 81    /// This should only be used for text that is displayed in a buffer,
 82    /// or other places that text needs to match the user's buffer font size.
 83    fn text_buffer(self, cx: &WindowContext) -> Self {
 84        let settings = ThemeSettings::get_global(cx);
 85        self.text_size(settings.buffer_font_size())
 86    }
 87}
 88
 89impl<E: Styled> StyledTypography for E {}
 90
 91/// A utility for getting the size of various semantic text sizes.
 92#[derive(Debug, Default, Clone)]
 93pub enum TextSize {
 94    /// The default size for UI text.
 95    ///
 96    /// `0.825rem` or `14px` at the default scale of `1rem` = `16px`.
 97    ///
 98    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
 99    #[default]
100    Default,
101    /// The large size for UI text.
102    ///
103    /// `1rem` or `16px` at the default scale of `1rem` = `16px`.
104    ///
105    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
106    Large,
107
108    /// The small size for UI text.
109    ///
110    /// `0.75rem` or `12px` at the default scale of `1rem` = `16px`.
111    ///
112    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
113    Small,
114
115    /// The extra small size for UI text.
116    ///
117    /// `0.625rem` or `10px` at the default scale of `1rem` = `16px`.
118    ///
119    /// Note: The absolute size of this text will change based on a user's `ui_scale` setting.
120    XSmall,
121
122    /// The `ui_font_size` set by the user.
123    Ui,
124    /// The `buffer_font_size` set by the user.
125    Editor,
126    // TODO: The terminal settings will need to be passed to
127    // ThemeSettings before we can enable this.
128    //// The `terminal.font_size` set by the user.
129    // Terminal,
130}
131
132impl TextSize {
133    /// Returns the text size in rems.
134    pub fn rems(self, cx: &AppContext) -> Rems {
135        let theme_settings = ThemeSettings::get_global(cx);
136
137        match self {
138            Self::Large => rems_from_px(16.),
139            Self::Default => rems_from_px(14.),
140            Self::Small => rems_from_px(12.),
141            Self::XSmall => rems_from_px(10.),
142            Self::Ui => rems_from_px(theme_settings.ui_font_size.into()),
143            Self::Editor => rems_from_px(theme_settings.buffer_font_size.into()),
144        }
145    }
146}
147
148/// The size of a [`Headline`] element
149///
150/// Defaults to a Major Second scale.
151#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
152pub enum HeadlineSize {
153    /// An extra small headline - `~14px` @16px/rem
154    XSmall,
155    /// A small headline - `16px` @16px/rem
156    Small,
157    #[default]
158    /// A medium headline - `~18px` @16px/rem
159    Medium,
160    /// A large headline - `~20px` @16px/rem
161    Large,
162    /// An extra large headline - `~22px` @16px/rem
163    XLarge,
164}
165
166impl HeadlineSize {
167    /// Returns the headline size in rems.
168    pub fn rems(self) -> Rems {
169        match self {
170            Self::XSmall => rems(0.88),
171            Self::Small => rems(1.0),
172            Self::Medium => rems(1.125),
173            Self::Large => rems(1.27),
174            Self::XLarge => rems(1.43),
175        }
176    }
177
178    /// Returns the line height for the headline size.
179    pub fn line_height(self) -> Rems {
180        match self {
181            Self::XSmall => rems(1.6),
182            Self::Small => rems(1.6),
183            Self::Medium => rems(1.6),
184            Self::Large => rems(1.6),
185            Self::XLarge => rems(1.6),
186        }
187    }
188}
189
190/// A headline element, used to emphasize some text and
191/// create a visual hierarchy.
192#[derive(IntoElement)]
193pub struct Headline {
194    size: HeadlineSize,
195    text: SharedString,
196    color: Color,
197}
198
199impl RenderOnce for Headline {
200    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
201        let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
202
203        div()
204            .font(ui_font)
205            .line_height(self.size.line_height())
206            .text_size(self.size.rems())
207            .text_color(cx.theme().colors().text)
208            .child(self.text)
209    }
210}
211
212impl Headline {
213    /// Create a new headline element.
214    pub fn new(text: impl Into<SharedString>) -> Self {
215        Self {
216            size: HeadlineSize::default(),
217            text: text.into(),
218            color: Color::default(),
219        }
220    }
221
222    /// Set the size of the headline.
223    pub fn size(mut self, size: HeadlineSize) -> Self {
224        self.size = size;
225        self
226    }
227
228    /// Set the color of the headline.
229    pub fn color(mut self, color: Color) -> Self {
230        self.color = color;
231        self
232    }
233}