use std::fmt; use font::Size; use serde::de::Visitor; use serde::{Deserialize, Deserializer}; #[cfg(target_os = "macos")] use crate::config::DefaultTrueBool; use crate::config::{failure_default, Delta}; /// Font config /// /// Defaults are provided at the level of this struct per platform, but not per /// field in this struct. It might be nice in the future to have defaults for /// each value independently. Alternatively, maybe erroring when the user /// doesn't provide complete config is Ok. #[serde(default)] #[derive(Debug, Deserialize, Clone, PartialEq, Eq)] pub struct Font { /// Normal font face #[serde(deserialize_with = "failure_default")] normal: FontDescription, /// Bold font face #[serde(deserialize_with = "failure_default")] bold: SecondaryFontDescription, /// Italic font face #[serde(deserialize_with = "failure_default")] italic: SecondaryFontDescription, /// Bold italic font face #[serde(deserialize_with = "failure_default")] bold_italic: SecondaryFontDescription, /// Font size in points #[serde(deserialize_with = "DeserializeSize::deserialize")] pub size: Size, /// Extra spacing per character #[serde(deserialize_with = "failure_default")] pub offset: Delta, /// Glyph offset within character cell #[serde(deserialize_with = "failure_default")] pub glyph_offset: Delta, #[cfg(target_os = "macos")] #[serde(deserialize_with = "failure_default")] use_thin_strokes: DefaultTrueBool, } impl Default for Font { fn default() -> Font { Font { size: default_font_size(), normal: Default::default(), bold: Default::default(), italic: Default::default(), bold_italic: Default::default(), glyph_offset: Default::default(), offset: Default::default(), #[cfg(target_os = "macos")] use_thin_strokes: Default::default(), } } } impl Font { /// Get a font clone with a size modification pub fn with_size(self, size: Size) -> Font { Font { size, ..self } } // Get normal font description pub fn normal(&self) -> &FontDescription { &self.normal } // Get bold font description pub fn bold(&self) -> FontDescription { self.bold.desc(&self.normal) } // Get italic font description pub fn italic(&self) -> FontDescription { self.italic.desc(&self.normal) } // Get bold italic font description pub fn bold_italic(&self) -> FontDescription { self.bold_italic.desc(&self.normal) } #[cfg(target_os = "macos")] pub fn use_thin_strokes(&self) -> bool { self.use_thin_strokes.0 } #[cfg(not(target_os = "macos"))] pub fn use_thin_strokes(&self) -> bool { false } } fn default_font_size() -> Size { Size::new(11.) } /// Description of the normal font #[serde(default)] #[derive(Debug, Deserialize, Clone, PartialEq, Eq)] pub struct FontDescription { #[serde(deserialize_with = "failure_default")] pub family: String, #[serde(deserialize_with = "failure_default")] pub style: Option, } impl Default for FontDescription { fn default() -> FontDescription { FontDescription { #[cfg(not(any(target_os = "macos", windows)))] family: "monospace".into(), #[cfg(target_os = "macos")] family: "Menlo".into(), #[cfg(windows)] family: "Consolas".into(), style: None, } } } /// Description of the italic and bold font #[serde(default)] #[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)] pub struct SecondaryFontDescription { #[serde(deserialize_with = "failure_default")] family: Option, #[serde(deserialize_with = "failure_default")] style: Option, } impl SecondaryFontDescription { pub fn desc(&self, fallback: &FontDescription) -> FontDescription { FontDescription { family: self.family.clone().unwrap_or_else(|| fallback.family.clone()), style: self.style.clone(), } } } trait DeserializeSize: Sized { fn deserialize<'a, D>(_: D) -> ::std::result::Result where D: serde::de::Deserializer<'a>; } impl DeserializeSize for Size { fn deserialize<'a, D>(deserializer: D) -> ::std::result::Result where D: serde::de::Deserializer<'a>, { use std::marker::PhantomData; struct NumVisitor<__D> { _marker: PhantomData<__D>, } impl<'a, __D> Visitor<'a> for NumVisitor<__D> where __D: serde::de::Deserializer<'a>, { type Value = f64; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("f64 or u64") } fn visit_f64(self, value: f64) -> ::std::result::Result where E: ::serde::de::Error, { Ok(value) } fn visit_u64(self, value: u64) -> ::std::result::Result where E: ::serde::de::Error, { Ok(value as f64) } } let value = serde_yaml::Value::deserialize(deserializer)?; let size = value .deserialize_any(NumVisitor:: { _marker: PhantomData }) .map(|v| Size::new(v as _)); // Use default font size as fallback match size { Ok(size) => Ok(size), Err(err) => { let size = default_font_size(); error!("Problem with config: {}; using size {}", err, size.as_f32_pts()); Ok(size) }, } } }