use bezel::{
gpui::{App, Pixels, SharedString, font, px},
theme::{Appearance, Theme},
};
use std::sync::RwLock;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Families {
pub sans: Option<SharedString>,
pub body: Option<SharedString>,
pub mono: Option<SharedString>,
}
static FAMILIES: RwLock<Option<Families>> = RwLock::new(None);
fn held() -> Families {
FAMILIES
.read()
.ok()
.and_then(|held| held.clone())
.unwrap_or_default()
}
pub fn families() -> Families {
held()
}
pub fn palette(appearance: Appearance) -> Theme {
let mut theme = Theme::for_appearance(appearance);
let families = held();
if let Some(sans) = families.sans {
theme.font_body = sans.clone();
theme.font_sans = sans;
}
if let Some(body) = families.body {
theme.font_body = body;
}
if let Some(mono) = families.mono {
theme.font_mono = mono;
}
theme
}
pub fn init(families: Families) {
if let Ok(mut held) = FAMILIES.write() {
*held = Some(families);
}
}
pub fn set(families: Families, cx: &mut App) {
init(families);
let appearance = Theme::of(cx).appearance;
Theme::install(appearance, cx);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Family {
pub name: SharedString,
pub mono: bool,
}
const PROBE: Pixels = px(64.);
static INSTALLED: RwLock<Option<Vec<Family>>> = RwLock::new(None);
pub fn installed(cx: &App) -> Vec<Family> {
if let Some(measured) = INSTALLED.read().ok().and_then(|held| held.clone()) {
return measured;
}
let text_system = cx.text_system();
let measured: Vec<Family> = text_system
.all_font_names()
.into_iter()
.filter(|name| !name.starts_with('.'))
.map(|name| {
let id = text_system.resolve_font(&font(name.clone()));
let advance = |ch| text_system.advance(id, PROBE, ch).map(|size| size.width);
let mono = match (advance('i'), advance('M'), advance('0')) {
(Ok(i), Ok(m), Ok(zero)) => i == m && m == zero,
_ => false,
};
Family {
name: name.into(),
mono,
}
})
.collect();
if let Ok(mut held) = INSTALLED.write() {
*held = Some(measured.clone());
}
measured
}
#[cfg(test)]
#[path = "../../tests/unit/fonts.rs"]
mod tests;