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}