use std::ops::Range;
use crate::text::{fontdb, Family, Stretch, Style, Weight};
use peniko::Color;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum FamilyOwned {
Name(String),
Serif,
SansSerif,
Cursive,
Fantasy,
Monospace,
}
impl FamilyOwned {
pub fn new(family: Family) -> Self {
match family {
Family::Name(name) => FamilyOwned::Name(name.to_string()),
Family::Serif => FamilyOwned::Serif,
Family::SansSerif => FamilyOwned::SansSerif,
Family::Cursive => FamilyOwned::Cursive,
Family::Fantasy => FamilyOwned::Fantasy,
Family::Monospace => FamilyOwned::Monospace,
}
}
pub fn as_family(&self) -> Family {
match self {
FamilyOwned::Name(name) => Family::Name(name),
FamilyOwned::Serif => Family::Serif,
FamilyOwned::SansSerif => Family::SansSerif,
FamilyOwned::Cursive => Family::Cursive,
FamilyOwned::Fantasy => Family::Fantasy,
FamilyOwned::Monospace => Family::Monospace,
}
}
pub fn parse_list(s: &str) -> impl Iterator<Item = FamilyOwned> + '_ + Clone {
ParseList {
source: s.as_bytes(),
len: s.len(),
pos: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LineHeightValue {
Normal(f32),
Px(f32),
}
#[derive(Clone, Debug)]
pub struct AttrsOwned {
attrs: cosmic_text::AttrsOwned,
pub font_size: f32,
line_height: LineHeightValue,
}
impl AttrsOwned {
pub fn new(attrs: Attrs) -> Self {
Self {
attrs: cosmic_text::AttrsOwned::new(attrs.attrs),
font_size: attrs.font_size,
line_height: attrs.line_height,
}
}
pub fn as_attrs(&self) -> Attrs {
Attrs {
attrs: self.attrs.as_attrs(),
font_size: self.font_size,
line_height: self.line_height,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Attrs<'a> {
attrs: cosmic_text::Attrs<'a>,
pub font_size: f32,
line_height: LineHeightValue,
}
impl Default for Attrs<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Attrs<'a> {
pub fn new() -> Self {
Self {
attrs: cosmic_text::Attrs::new(),
font_size: 16.0,
line_height: LineHeightValue::Normal(1.0),
}
}
pub fn color(mut self, color: Color) -> Self {
let c = color.to_rgba8();
self.attrs = self
.attrs
.color(cosmic_text::Color::rgba(c.r, c.g, c.b, c.a));
self
}
pub fn family(mut self, family: &'a [FamilyOwned]) -> Self {
if let Some(family) = family.first() {
self.attrs = self.attrs.family(family.as_family());
}
self
}
pub fn stretch(mut self, stretch: Stretch) -> Self {
self.attrs = self.attrs.stretch(stretch);
self
}
pub fn style(mut self, style: Style) -> Self {
self.attrs = self.attrs.style(style);
self
}
pub fn weight(mut self, weight: Weight) -> Self {
self.attrs = self.attrs.weight(weight);
self
}
pub fn raw_weight(mut self, weight: u16) -> Self {
self.attrs = self.attrs.weight(Weight(weight));
self
}
fn get_metrics(&self) -> cosmic_text::Metrics {
let line_height = match self.line_height {
LineHeightValue::Normal(n) => self.font_size * n,
LineHeightValue::Px(n) => n,
};
cosmic_text::Metrics::new(self.font_size, line_height)
}
pub fn font_size(mut self, font_size: f32) -> Self {
self.font_size = font_size;
self.attrs = self.attrs.metrics(self.get_metrics());
self
}
pub fn line_height(mut self, line_height: LineHeightValue) -> Self {
self.line_height = line_height;
self.attrs = self.attrs.metrics(self.get_metrics());
self
}
pub fn metadata(mut self, metadata: usize) -> Self {
self.attrs = self.attrs.metadata(metadata);
self
}
pub fn matches(&self, face: &fontdb::FaceInfo) -> bool {
self.attrs.matches(face)
}
pub fn compatible(&self, other: &Self) -> bool {
self.attrs.compatible(&other.attrs)
}
}
#[derive(PartialEq, Clone)]
pub struct AttrsList(pub cosmic_text::AttrsList);
impl AttrsList {
pub fn new(defaults: Attrs) -> Self {
Self(cosmic_text::AttrsList::new(defaults.attrs))
}
pub fn defaults(&self) -> Attrs {
self.0.defaults().into()
}
pub fn clear_spans(&mut self) {
self.0.clear_spans();
}
pub fn add_span(&mut self, range: Range<usize>, attrs: Attrs) {
self.0.add_span(range, attrs.attrs);
}
pub fn get_span(&self, index: usize) -> Attrs {
self.0.get_span(index).into()
}
pub fn split_off(&mut self, index: usize) -> Self {
let new = self.0.split_off(index);
Self(new)
}
}
impl<'a> From<cosmic_text::Attrs<'a>> for Attrs<'a> {
fn from(attrs: cosmic_text::Attrs<'a>) -> Self {
Self {
attrs,
font_size: 1.0,
line_height: LineHeightValue::Normal(1.0),
}
}
}
#[derive(Clone)]
struct ParseList<'a> {
source: &'a [u8],
len: usize,
pos: usize,
}
impl Iterator for ParseList<'_> {
type Item = FamilyOwned;
fn next(&mut self) -> Option<Self::Item> {
let mut quote = None;
let mut pos = self.pos;
while pos < self.len && {
let ch = self.source[pos];
ch.is_ascii_whitespace() || ch == b','
} {
pos += 1;
}
self.pos = pos;
if pos >= self.len {
return None;
}
let first = self.source[pos];
let mut start = pos;
match first {
b'"' | b'\'' => {
quote = Some(first);
pos += 1;
start += 1;
}
_ => {}
}
if let Some(quote) = quote {
while pos < self.len {
if self.source[pos] == quote {
self.pos = pos + 1;
return Some(FamilyOwned::Name(
core::str::from_utf8(self.source.get(start..pos)?)
.ok()?
.trim()
.to_string(),
));
}
pos += 1;
}
self.pos = pos;
return Some(FamilyOwned::Name(
core::str::from_utf8(self.source.get(start..pos)?)
.ok()?
.trim()
.to_string(),
));
}
let mut end = start;
while pos < self.len {
if self.source[pos] == b',' {
pos += 1;
break;
}
pos += 1;
end += 1;
}
self.pos = pos;
let name = core::str::from_utf8(self.source.get(start..end)?)
.ok()?
.trim();
Some(match name.to_lowercase().as_str() {
"serif" => FamilyOwned::Serif,
"sans-serif" => FamilyOwned::SansSerif,
"monospace" => FamilyOwned::Monospace,
"cursive" => FamilyOwned::Cursive,
"fantasy" => FamilyOwned::Fantasy,
_ => FamilyOwned::Name(name.to_string()),
})
}
}