use parley::{
Alignment, AlignmentOptions, FontContext, FontFamily, FontWeight, Layout as Text,
LayoutContext, LineHeight, StyleProperty,
};
use std::collections::{BTreeSet, HashMap};
use taffy::prelude::*;
use kurbo::Rect as KRect;
use peniko::ImageData;
use crate::display::{Draw, Frame, Glyph, Paint};
use crate::fonts::{self, FontFiles, FontId};
use crate::scene::{self, ImageId, Origin, TextAlign};
mod drawing;
#[cfg(test)]
mod examples;
mod layout;
mod painter;
#[cfg(test)]
pub(crate) mod testing;
mod update;
use drawing::*;
pub use drawing::{Damage, Drawing};
use layout::*;
use painter::*;
const FONTS: [&[u8]; 4] = [
include_bytes!("../../fonts/Inter-Regular.ttf"),
include_bytes!("../../fonts/Inter-Medium.ttf"),
include_bytes!("../../fonts/Inter-SemiBold.ttf"),
include_bytes!("../../fonts/Inter-Bold.ttf"),
];
pub struct Renderer {
fonts: FontContext,
inter: Vec<parley::fontique::Blob<u8>>,
registered: Option<BTreeSet<FontId>>,
layouts: LayoutContext<()>,
blobs: HashMap<ImageId, ImageData>,
painted: Vec<(Painted, Origin)>,
}
impl Default for Renderer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Undrawable {
pub message: String,
pub at: Option<Origin>,
}
impl std::fmt::Display for Undrawable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.message)
}
}
impl Renderer {
pub fn new() -> Renderer {
let fonts = FontContext {
collection: parley::fontique::Collection::new(parley::fontique::CollectionOptions {
shared: false,
system_fonts: false,
}),
source_cache: parley::fontique::SourceCache::default(),
};
let mut renderer = Renderer {
fonts,
inter: FONTS
.iter()
.map(|font| parley::fontique::Blob::new(std::sync::Arc::new(*font)))
.collect(),
registered: None,
layouts: LayoutContext::new(),
blobs: HashMap::new(),
painted: Vec::new(),
};
renderer.register(&FontFiles::new());
renderer
}
fn register(&mut self, files: &FontFiles) {
if let Some(registered) = &self.registered
&& files.keys().all(|id| registered.contains(id))
{
return;
}
let collection = &mut self.fonts.collection;
collection.clear();
for blob in &self.inter {
collection.register_fonts(blob.clone(), None);
}
for (id, file) in files {
let family = id.family();
collection.register_fonts(
file.blob.clone(),
Some(parley::fontique::FontInfoOverride {
family_name: Some(&family),
..Default::default()
}),
);
}
self.registered = Some(files.keys().copied().collect());
}
fn text_layout(&mut self, text: &scene::TextStyle, width: Option<f32>) -> Text<()> {
let mut builder = self
.layouts
.ranged_builder(&mut self.fonts, &text.content, 1.0, true);
let family = match text.font {
Some(id) => id.family(),
None => fonts::INTER.to_string(),
};
builder.push_default(StyleProperty::FontFamily(FontFamily::named(&family)));
builder.push_default(StyleProperty::FontSize(text.size));
builder.push_default(StyleProperty::FontWeight(FontWeight::new(
text.weight as f32,
)));
builder.push_default(StyleProperty::LineHeight(LineHeight::FontSizeRelative(
text.line_height,
)));
let mut layout = builder.build(&text.content);
layout.break_all_lines(width);
let align = match text.align {
TextAlign::Left => Alignment::Left,
TextAlign::Center => Alignment::Center,
TextAlign::Right => Alignment::Right,
};
layout.align(align, AlignmentOptions::default());
layout
}
}
fn frame_rect(f: Frame) -> KRect {
KRect::new(
f.x as f64,
f.y as f64,
(f.x + f.width) as f64,
(f.y + f.height) as f64,
)
}