autom8/ui/gui/
typography.rs1use eframe::egui::{self, FontData, FontDefinitions, FontFamily, FontId, TextStyle};
7use std::collections::BTreeMap;
8use std::sync::Arc;
9
10const GEIST_REGULAR: &[u8] = include_bytes!("fonts/Geist-Regular.ttf");
12
13const GEIST_MEDIUM: &[u8] = include_bytes!("fonts/Geist-Medium.ttf");
15
16const GEIST_SEMIBOLD: &[u8] = include_bytes!("fonts/Geist-SemiBold.ttf");
18
19const GEIST_MONO_REGULAR: &[u8] = include_bytes!("fonts/GeistMono-Regular.ttf");
21
22pub const FAMILY_GEIST_REGULAR: &str = "Geist-Regular";
24
25pub const FAMILY_GEIST_MEDIUM: &str = "Geist-Medium";
27
28pub const FAMILY_GEIST_SEMIBOLD: &str = "Geist-SemiBold";
30
31pub const FAMILY_GEIST_MONO: &str = "Geist-Mono";
33
34#[derive(Debug, Clone, Copy, PartialEq)]
39pub enum FontSize {
40 Caption,
42 Small,
44 Body,
46 Large,
48 Heading,
50 Title,
52 Display,
54}
55
56impl FontSize {
57 pub fn pixels(self) -> f32 {
59 match self {
60 FontSize::Caption => 10.0,
61 FontSize::Small => 12.0,
62 FontSize::Body => 14.0,
63 FontSize::Large => 16.0,
64 FontSize::Heading => 18.0,
65 FontSize::Title => 24.0,
66 FontSize::Display => 32.0,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq)]
73pub enum FontWeight {
74 Regular,
76 Medium,
78 SemiBold,
80}
81
82impl FontWeight {
83 pub fn family(self) -> FontFamily {
85 match self {
86 FontWeight::Regular => FontFamily::Name(FAMILY_GEIST_REGULAR.into()),
87 FontWeight::Medium => FontFamily::Name(FAMILY_GEIST_MEDIUM.into()),
88 FontWeight::SemiBold => FontFamily::Name(FAMILY_GEIST_SEMIBOLD.into()),
89 }
90 }
91}
92
93pub fn font(size: FontSize, weight: FontWeight) -> FontId {
95 FontId::new(size.pixels(), weight.family())
96}
97
98pub fn mono(size: FontSize) -> FontId {
100 FontId::new(size.pixels(), FontFamily::Name(FAMILY_GEIST_MONO.into()))
101}
102
103pub fn line_height(size: FontSize) -> f32 {
108 size.pixels() * 1.4
109}
110
111pub fn configure_fonts() -> FontDefinitions {
119 let mut fonts = FontDefinitions::default();
120
121 fonts.font_data.insert(
123 FAMILY_GEIST_REGULAR.to_owned(),
124 Arc::new(FontData::from_static(GEIST_REGULAR)),
125 );
126 fonts.font_data.insert(
127 FAMILY_GEIST_MEDIUM.to_owned(),
128 Arc::new(FontData::from_static(GEIST_MEDIUM)),
129 );
130 fonts.font_data.insert(
131 FAMILY_GEIST_SEMIBOLD.to_owned(),
132 Arc::new(FontData::from_static(GEIST_SEMIBOLD)),
133 );
134 fonts.font_data.insert(
135 FAMILY_GEIST_MONO.to_owned(),
136 Arc::new(FontData::from_static(GEIST_MONO_REGULAR)),
137 );
138
139 fonts.families.insert(
141 FontFamily::Name(FAMILY_GEIST_REGULAR.into()),
142 vec![FAMILY_GEIST_REGULAR.to_owned()],
143 );
144 fonts.families.insert(
145 FontFamily::Name(FAMILY_GEIST_MEDIUM.into()),
146 vec![FAMILY_GEIST_MEDIUM.to_owned()],
147 );
148 fonts.families.insert(
149 FontFamily::Name(FAMILY_GEIST_SEMIBOLD.into()),
150 vec![FAMILY_GEIST_SEMIBOLD.to_owned()],
151 );
152 fonts.families.insert(
153 FontFamily::Name(FAMILY_GEIST_MONO.into()),
154 vec![FAMILY_GEIST_MONO.to_owned()],
155 );
156
157 fonts
159 .families
160 .entry(FontFamily::Proportional)
161 .or_default()
162 .insert(0, FAMILY_GEIST_REGULAR.to_owned());
163
164 fonts
166 .families
167 .entry(FontFamily::Monospace)
168 .or_default()
169 .insert(0, FAMILY_GEIST_MONO.to_owned());
170
171 fonts
172}
173
174pub fn configure_text_styles(ctx: &egui::Context) {
179 let mut style = (*ctx.style()).clone();
180
181 let mut text_styles = BTreeMap::new();
183 text_styles.insert(
184 TextStyle::Small,
185 FontId::new(FontSize::Small.pixels(), FontFamily::Proportional),
186 );
187 text_styles.insert(
188 TextStyle::Body,
189 FontId::new(FontSize::Body.pixels(), FontFamily::Proportional),
190 );
191 text_styles.insert(
192 TextStyle::Button,
193 FontId::new(FontSize::Body.pixels(), FontWeight::Medium.family()),
194 );
195 text_styles.insert(
196 TextStyle::Heading,
197 FontId::new(FontSize::Heading.pixels(), FontWeight::SemiBold.family()),
198 );
199 text_styles.insert(
200 TextStyle::Monospace,
201 FontId::new(FontSize::Body.pixels(), FontFamily::Monospace),
202 );
203
204 style.text_styles = text_styles;
205 ctx.set_style(style);
206}
207
208pub fn init(ctx: &egui::Context) {
213 ctx.set_fonts(configure_fonts());
214 configure_text_styles(ctx);
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_font_size_pixels() {
223 assert_eq!(FontSize::Caption.pixels(), 10.0);
224 assert_eq!(FontSize::Small.pixels(), 12.0);
225 assert_eq!(FontSize::Body.pixels(), 14.0);
226 assert_eq!(FontSize::Large.pixels(), 16.0);
227 assert_eq!(FontSize::Heading.pixels(), 18.0);
228 assert_eq!(FontSize::Title.pixels(), 24.0);
229 assert_eq!(FontSize::Display.pixels(), 32.0);
230 }
231
232 #[test]
233 fn test_font_weight_family() {
234 assert_eq!(
235 FontWeight::Regular.family(),
236 FontFamily::Name(FAMILY_GEIST_REGULAR.into())
237 );
238 assert_eq!(
239 FontWeight::Medium.family(),
240 FontFamily::Name(FAMILY_GEIST_MEDIUM.into())
241 );
242 assert_eq!(
243 FontWeight::SemiBold.family(),
244 FontFamily::Name(FAMILY_GEIST_SEMIBOLD.into())
245 );
246 }
247
248 #[test]
249 fn test_font_helper() {
250 let font_id = font(FontSize::Body, FontWeight::Regular);
251 assert_eq!(font_id.size, 14.0);
252 assert_eq!(
253 font_id.family,
254 FontFamily::Name(FAMILY_GEIST_REGULAR.into())
255 );
256 }
257
258 #[test]
259 fn test_mono_helper() {
260 let font_id = mono(FontSize::Body);
261 assert_eq!(font_id.size, 14.0);
262 assert_eq!(font_id.family, FontFamily::Name(FAMILY_GEIST_MONO.into()));
263 }
264
265 #[test]
266 fn test_configure_fonts_has_all_families() {
267 let fonts = configure_fonts();
268
269 assert!(fonts.font_data.contains_key(FAMILY_GEIST_REGULAR));
271 assert!(fonts.font_data.contains_key(FAMILY_GEIST_MEDIUM));
272 assert!(fonts.font_data.contains_key(FAMILY_GEIST_SEMIBOLD));
273 assert!(fonts.font_data.contains_key(FAMILY_GEIST_MONO));
274
275 assert!(fonts
277 .families
278 .contains_key(&FontFamily::Name(FAMILY_GEIST_REGULAR.into())));
279 assert!(fonts
280 .families
281 .contains_key(&FontFamily::Name(FAMILY_GEIST_MEDIUM.into())));
282 assert!(fonts
283 .families
284 .contains_key(&FontFamily::Name(FAMILY_GEIST_SEMIBOLD.into())));
285 assert!(fonts
286 .families
287 .contains_key(&FontFamily::Name(FAMILY_GEIST_MONO.into())));
288
289 let proportional = fonts.families.get(&FontFamily::Proportional).unwrap();
291 assert_eq!(proportional.first().unwrap(), FAMILY_GEIST_REGULAR);
292
293 let monospace = fonts.families.get(&FontFamily::Monospace).unwrap();
294 assert_eq!(monospace.first().unwrap(), FAMILY_GEIST_MONO);
295 }
296}