//! Produce text!!!
use std::collections::HashMap;
use assets_manager::{
loader::{BytesLoader, LoadFrom},
Asset, AssetCache, ReloadId,
};
use glyphon::{
Attrs, Buffer, Color, FontSystem, Metrics, Shaping, SwashCache, TextArea, TextBounds,
};
/// Contains the font system, swash cache, and is generally permanent
pub struct TextSystem {
pub fonts: FontSystem,
pub swash: SwashCache,
loaded_fonts: HashMap<String, ReloadId>,
}
impl Default for TextSystem {
fn default() -> Self {
Self {
fonts: FontSystem::new(),
swash: SwashCache::new(),
loaded_fonts: HashMap::new(),
}
}
}
struct FontData(Vec<u8>);
impl From<Vec<u8>> for FontData {
fn from(value: Vec<u8>) -> Self {
Self(value)
}
}
impl Asset for FontData {
const EXTENSIONS: &'static [&'static str] = &["ttf"];
type Loader = LoadFrom<Vec<u8>, BytesLoader>;
}
impl TextSystem {
/// Loading requested asset fonts here
pub fn prepare(&mut self, _data: &TextSystemData, assets: &mut AssetCache) {
let font_name = "fonts.poiret_one.PoiretOne-Regular";
let font_data_handle = match assets.load::<FontData>(font_name) {
Ok(hnd) => hnd,
Err(e) => {
log::error!("failed to load font {font_name}: {e}");
return;
}
};
let current_reload_id = self.loaded_fonts.get(font_name);
if current_reload_id.is_none()
|| current_reload_id.is_some_and(|old_id| old_id < &font_data_handle.last_reload_id())
{
// Load the font into the system
self.loaded_fonts
.insert(font_name.to_string(), font_data_handle.last_reload_id());
self.fonts
.db_mut()
.load_font_data(font_data_handle.read().0.clone());
log::info!("successfully loaded font {font_name}!");
}
}
/// Produce world space text.
///
/// Moving the camera *should* move this text.
pub fn produce_world(
&mut self,
_data: &TextSystemData,
) -> impl IntoIterator<Item = (Buffer, BufferMapper)> {
vec![todo!()]
}
/// Produce camera space text.
///
/// Moving the game camera should *not* move this text.
pub fn produce_camera(
&mut self,
_data: &TextSystemData,
) -> impl IntoIterator<Item = (Buffer, BufferMapper)> {
let metrics = Metrics::new(32.0, 20.0);
let mut buffer = Buffer::new(&mut self.fonts, metrics);
{
let attrs = Attrs::new().family(glyphon::Family::Name("Poiret One"));
let mut buffer = buffer.borrow_with(&mut self.fonts);
buffer.set_size(200.0, 25.0);
buffer.set_text("Hello, Rust! 🦀\n", attrs, Shaping::Advanced);
buffer.shape_until_scroll();
}
vec![(buffer, BufferMapper)]
}
}
/// Resposible for configuring the TextArea of a given buffer
pub struct BufferMapper;
impl BufferMapper {
pub(crate) fn produce<'a>(&self, buffer: &'a Buffer) -> TextArea<'a> {
TextArea {
buffer,
left: 32.0,
top: 32.0,
scale: 1.0,
bounds: TextBounds {
left: 0,
top: 0,
..Default::default()
},
default_color: Color::rgba(255, 0, 0, 200),
}
}
}
/// Contains ephemural data about what the game wants to display with text
pub struct TextSystemData {}
impl TextSystemData {}