use std::fmt;
use std::str;
use super::wish;
#[derive(Clone, Default, Debug, PartialEq)]
pub enum Weight {
#[default]
Normal,
Bold,
}
impl fmt::Display for Weight {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = match self {
Weight::Normal => "normal",
Weight::Bold => "bold",
};
write!(f, "{}", &value)
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub enum Slant {
Italic,
#[default]
Roman,
}
impl fmt::Display for Slant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = match self {
Slant::Italic => "italic",
Slant::Roman => "roman",
};
write!(f, "{}", &value)
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct TkFontMetrics {
pub ascent: u64,
pub descent: u64,
pub line_space: u64,
pub fixed: bool,
}
impl fmt::Display for TkFontMetrics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut font = String::new();
font.push_str(&format!("-ascent {} ", self.ascent));
font.push_str(&format!("-descent {} ", self.descent));
font.push_str(&format!("-linespace {} ", self.line_space));
font.push_str(&format!("-fixed {} ", if self.fixed { "1" } else { "0" }));
write!(f, "{}", &font)
}
}
#[derive(Debug)]
pub struct ParseFontMetricsErr;
impl str::FromStr for TkFontMetrics {
type Err = ParseFontMetricsErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut font = TkFontMetrics {
..Default::default()
};
for part in s.split('-') {
if part.starts_with("ascent") {
if let Ok(size) = &part[7..].trim().parse::<u64>() {
font.ascent = *size;
}
} else if part.starts_with("descent") {
if let Ok(size) = &part[8..].trim().parse::<u64>() {
font.descent = *size;
}
} else if part.starts_with("linespace") {
if let Ok(size) = &part[10..].trim().parse::<u64>() {
font.line_space = *size;
}
} else if part.starts_with("fixed 1") {
font.fixed = true;
}
}
Ok(font)
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct TkFont {
pub family: String,
pub size: u64,
pub weight: Weight,
pub slant: Slant,
pub underline: bool,
pub overstrike: bool,
}
impl fmt::Display for TkFont {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut font = String::new();
font.push_str(&format!("-family {{{}}} ", self.family));
font.push_str(&format!("-size {} ", self.size));
font.push_str(&format!("-weight {} ", self.weight));
font.push_str(&format!("-slant {} ", self.slant));
font.push_str(&format!(
"-underline {} ",
if self.underline { "1" } else { "0" }
));
font.push_str(&format!(
"-overstrike {} ",
if self.overstrike { "1" } else { "0" }
));
write!(f, "{}", &font)
}
}
#[derive(Debug)]
pub struct ParseFontErr;
impl str::FromStr for TkFont {
type Err = ParseFontErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut font = TkFont {
..Default::default()
};
for part in s.split('-') {
if part.starts_with("family") {
let mut family = String::from(&part[7..]);
if let Some(index) = family.find('{') {
family.remove(index);
}
if let Some(index) = family.find('}') {
family.remove(index);
}
family = String::from(family.trim());
font.family = family;
} else if part.starts_with("size") {
if let Ok(size) = &part[5..].trim().parse::<u64>() {
font.size = *size;
}
} else if part.starts_with("weight bold") {
font.weight = Weight::Bold;
} else if part.starts_with("slant italic") {
font.slant = Slant::Italic;
} else if part.starts_with("underline 1") {
font.underline = true;
} else if part.starts_with("overstrike 1") {
font.overstrike = true;
}
}
Ok(font)
}
}
impl TkFont {
pub fn measure(&self, text: &str) -> u64 {
let msg = format!(
"puts [font measure {{{}}} {{{}}}] ; flush stdout",
self, text
);
let result = wish::ask_wish(&msg);
result.parse::<u64>().unwrap_or(0)
}
pub fn metrics(&self) -> TkFontMetrics {
let msg = format!("puts [font metrics {{{}}}] ; flush stdout", self);
let result = wish::ask_wish(&msg);
if let Ok(value) = result.parse::<TkFontMetrics>() {
value
} else {
TkFontMetrics {
..Default::default()
}
}
}
}
fn font_from_name(name: &str) -> TkFont {
let msg = format!("puts [font actual {}] ; flush stdout", name);
let result = wish::ask_wish(&msg);
result.parse::<TkFont>().unwrap_or(TkFont {
..Default::default()
})
}
pub fn tk_default_font() -> TkFont {
font_from_name("TkDefaultFont")
}
pub fn tk_text_font() -> TkFont {
font_from_name("TkTextFont")
}
pub fn tk_fixed_font() -> TkFont {
font_from_name("TkFixedFont")
}
pub fn tk_menu_font() -> TkFont {
font_from_name("TkMenuFont")
}
pub fn tk_heading_font() -> TkFont {
font_from_name("TkHeadingFont")
}
pub fn tk_caption_font() -> TkFont {
font_from_name("TkCaptionFont")
}
pub fn tk_small_caption_font() -> TkFont {
font_from_name("TkSmallCaptionFont")
}
pub fn tk_icon_font() -> TkFont {
font_from_name("TkIconFont")
}
pub fn tk_tooltip_font() -> TkFont {
font_from_name("TkTooltipFont")
}
pub fn font_families() -> Vec<String> {
let result = wish::ask_wish("puts [font families] ; flush stdout");
wish::split_items(&result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn font_to_str() {
let font = TkFont {
family: String::from("Helvetica"),
size: 14,
..Default::default()
};
let font_str = font.to_string();
assert_eq!(
"-family {Helvetica} -size 14 -weight normal -slant roman -underline 0 -overstrike 0 ",
font_str
);
}
#[test]
fn font_to_str_2() {
let font = TkFont {
family: String::from("Helvetica Special"),
size: 14,
..Default::default()
};
let font_str = font.to_string();
assert_eq!("-family {Helvetica Special} -size 14 -weight normal -slant roman -underline 0 -overstrike 0 ",
font_str);
}
#[test]
fn str_to_font() {
let font_str =
"-family {Helvetica} -size 14 -weight normal -slant italic -underline 0 -overstrike 1";
let font = font_str.parse::<TkFont>().unwrap();
assert_eq!(
TkFont {
family: String::from("Helvetica"),
size: 14,
slant: Slant::Italic,
overstrike: true,
..Default::default()
},
font
);
}
#[test]
fn str_to_font_2() {
let font_str = "-family {Helvetica Special} -size 14 -weight normal -slant italic -underline 0 -overstrike 1";
let font = font_str.parse::<TkFont>().unwrap();
assert_eq!(
TkFont {
family: String::from("Helvetica Special"),
size: 14,
slant: Slant::Italic,
overstrike: true,
..Default::default()
},
font
);
}
}