use std::collections::BTreeMap;
use egui::{FontFamily, FontId, TextStyle};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum UiFont {
SanFrancisco,
SegoeUi,
Inter,
System,
}
impl UiFont {
pub fn as_str(self) -> &'static str {
match self {
UiFont::SanFrancisco => "San Francisco",
UiFont::SegoeUi => "Segoe UI Variable",
UiFont::Inter => "Inter",
UiFont::System => "System",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Typography {
pub ui_font: UiFont,
pub base: f32,
pub ratio: f32,
pub line_height: f32,
}
impl Default for Typography {
fn default() -> Self {
Self { ui_font: UiFont::System, base: 14.0, ratio: 1.22, line_height: 1.45 }
}
}
impl Typography {
pub fn with_font(mut self, f: UiFont) -> Self {
self.ui_font = f;
self
}
pub fn step(&self, n: i32) -> f32 {
(self.base * self.ratio.powi(n)).round()
}
pub fn text_styles(&self) -> BTreeMap<TextStyle, FontId> {
let mut m = BTreeMap::new();
m.insert(TextStyle::Heading, FontId::new(self.step(2), FontFamily::Proportional));
m.insert(TextStyle::Body, FontId::new(self.step(0), FontFamily::Proportional));
m.insert(TextStyle::Button, FontId::new(self.step(0), FontFamily::Proportional));
m.insert(TextStyle::Small, FontId::new(self.step(-1), FontFamily::Proportional));
m.insert(TextStyle::Monospace, FontId::new(self.step(0), FontFamily::Monospace));
m
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scale_steps_grow_and_shrink_monotonically() {
let t = Typography::default();
assert!(t.step(-1) < t.step(0));
assert!(t.step(0) < t.step(1));
assert!(t.step(1) < t.step(2));
assert_eq!(t.step(0), t.base.round());
}
#[test]
fn text_styles_cover_the_core_roles() {
let m = Typography::default().text_styles();
for s in [TextStyle::Heading, TextStyle::Body, TextStyle::Button, TextStyle::Small, TextStyle::Monospace] {
assert!(m.contains_key(&s), "missing {s:?}");
}
assert!(m[&TextStyle::Heading].size > m[&TextStyle::Body].size);
assert!(m[&TextStyle::Small].size < m[&TextStyle::Body].size);
}
}