use std::fmt;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use cutty_config_derive::{ConfigDeserialize, SerdeReplace};
use crate::config::ui_config::Delta;
#[derive(ConfigDeserialize, Serialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct Font {
pub offset: Delta<i8>,
pub glyph_offset: Delta<i8>,
normal: FontDescription,
bold: SecondaryFontDescription,
italic: SecondaryFontDescription,
bold_italic: SecondaryFontDescription,
size: Size,
}
impl Font {
pub fn with_size(self, size: FontSize) -> Font {
Font { size: Size(size), ..self }
}
#[inline]
pub fn size(&self) -> FontSize {
self.size.0
}
pub fn normal(&self) -> &FontDescription {
&self.normal
}
pub fn bold(&self) -> FontDescription {
self.bold.desc(&self.normal)
}
pub fn italic(&self) -> FontDescription {
self.italic.desc(&self.normal)
}
pub fn bold_italic(&self) -> FontDescription {
self.bold_italic.desc(&self.normal)
}
}
#[derive(ConfigDeserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct FontDescription {
pub family: String,
pub style: Option<String>,
}
impl Default for FontDescription {
fn default() -> FontDescription {
FontDescription { family: "FiraMono Nerd Font".into(), style: None }
}
}
#[derive(ConfigDeserialize, Serialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct SecondaryFontDescription {
family: Option<String>,
style: Option<String>,
}
impl SecondaryFontDescription {
pub fn desc(&self, fallback: &FontDescription) -> FontDescription {
FontDescription {
family: self.family.clone().unwrap_or_else(|| fallback.family.clone()),
style: self.style.clone(),
}
}
}
#[derive(SerdeReplace, Debug, Clone, PartialEq, Eq)]
struct Size(FontSize);
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq)]
pub struct FontSize(f32);
impl Eq for FontSize {}
impl FontSize {
pub const fn new(size: f32) -> Self {
Self(size)
}
pub const fn from_px(size: f32) -> Self {
Self(size)
}
pub const fn as_px(self) -> f32 {
self.0
}
pub const fn as_pt(self) -> f32 {
self.0
}
pub const fn scale(self, factor: f32) -> Self {
Self(self.0 * factor)
}
}
impl Default for Size {
fn default() -> Self {
Self(FontSize::new(11.25))
}
}
impl<'de> Deserialize<'de> for Size {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct NumVisitor;
impl Visitor<'_> for NumVisitor {
type Value = Size;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("f64 or i64")
}
fn visit_f64<E: de::Error>(self, value: f64) -> Result<Self::Value, E> {
Ok(Size(FontSize::new(value as f32)))
}
fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E> {
Ok(Size(FontSize::new(value as f32)))
}
}
deserializer.deserialize_any(NumVisitor)
}
}
impl Serialize for Size {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_f32(self.0.as_pt())
}
}