typography.rs

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