use cosmic_text::{Align, Attrs, Ellipsize, EllipsizeHeightLimit, Weight};
use std::borrow::Cow;
use std::hash::{Hash, Hasher};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TextAlign {
Left,
Center,
Right,
Justify,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TextTransform {
Upper,
Lower,
Capitalize,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TextTraits {
pub weight: u16,
pub align: Option<TextAlign>,
pub tracking: f32,
pub lines: u16,
pub transform: Option<TextTransform>,
}
impl Default for TextTraits {
fn default() -> Self {
Self { weight: 400, align: None, tracking: 0.0, lines: 0, transform: None }
}
}
impl Hash for TextTraits {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.weight, self.align, self.tracking.to_bits(), self.lines, self.transform).hash(state);
}
}
impl TextTraits {
pub(crate) fn apply(&self, mut attrs: Attrs<'static>) -> Attrs<'static> {
if self.weight != 400 {
attrs = attrs.weight(Weight(self.weight));
}
if self.tracking != 0.0 {
attrs = attrs.letter_spacing(self.tracking);
}
attrs
}
pub(crate) fn alignment(&self) -> Option<Align> {
self.align.map(|align| match align {
TextAlign::Left => Align::Left,
TextAlign::Center => Align::Center,
TextAlign::Right => Align::Right,
TextAlign::Justify => Align::Justified,
})
}
pub(crate) fn ellipsize(&self) -> Ellipsize {
match self.lines {
0 => Ellipsize::None,
lines => Ellipsize::End(EllipsizeHeightLimit::Lines(usize::from(lines))),
}
}
pub(crate) fn transformed<'a>(&self, text: &'a str, rich: bool) -> Cow<'a, str> {
match self.transform {
Some(_) if rich => Cow::Borrowed(text),
Some(TextTransform::Upper) => Cow::Owned(text.to_uppercase()),
Some(TextTransform::Lower) => Cow::Owned(text.to_lowercase()),
Some(TextTransform::Capitalize) => Cow::Owned(capitalize(text)),
None => Cow::Borrowed(text),
}
}
}
fn capitalize(text: &str) -> String {
let mut start = true;
text.chars()
.flat_map(|c| {
let upper = start && c.is_alphabetic();
start = c.is_whitespace();
if upper { c.to_uppercase().collect::<Vec<_>>() } else { vec![c] }
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn por_defecto_no_cambia_nada() {
let traits = TextTraits::default();
assert_eq!(traits.ellipsize(), Ellipsize::None);
assert!(traits.alignment().is_none());
assert_eq!(traits.transformed("Hola", false), "Hola");
}
#[test]
fn capitalizacion() {
let mut traits = TextTraits { transform: Some(TextTransform::Capitalize), ..Default::default() };
assert_eq!(traits.transformed("hola mundo azul", false), "Hola Mundo Azul");
traits.transform = Some(TextTransform::Upper);
assert_eq!(traits.transformed("ñandú", false), "ÑANDÚ");
assert_eq!(traits.transformed("ñandú", true), "ñandú", "con tramos no se toca");
}
#[test]
fn truncar_es_una_linea() {
let traits = TextTraits { lines: 1, ..Default::default() };
assert_eq!(traits.ellipsize(), Ellipsize::End(EllipsizeHeightLimit::Lines(1)));
}
}