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)),
144 Self::Editor => rems_from_px(theme_settings.buffer_font_size(cx)),
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, RegisterComponent)]
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
236impl Component for Headline {
237 fn scope() -> ComponentScope {
238 ComponentScope::Typography
239 }
240
241 fn description() -> Option<&'static str> {
242 Some("A headline element used to emphasize text and create visual hierarchy in the UI.")
243 }
244
245 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
246 Some(
247 v_flex()
248 .gap_1()
249 .children(vec![
250 single_example(
251 "XLarge",
252 Headline::new("XLarge Headline")
253 .size(HeadlineSize::XLarge)
254 .into_any_element(),
255 ),
256 single_example(
257 "Large",
258 Headline::new("Large Headline")
259 .size(HeadlineSize::Large)
260 .into_any_element(),
261 ),
262 single_example(
263 "Medium (Default)",
264 Headline::new("Medium Headline").into_any_element(),
265 ),
266 single_example(
267 "Small",
268 Headline::new("Small Headline")
269 .size(HeadlineSize::Small)
270 .into_any_element(),
271 ),
272 single_example(
273 "XSmall",
274 Headline::new("XSmall Headline")
275 .size(HeadlineSize::XSmall)
276 .into_any_element(),
277 ),
278 ])
279 .into_any_element(),
280 )
281 }
282}