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