mod bmfont;
mod cache;
mod packer;
#[cfg(feature = "font_ttf")]
mod vector;
use std::cell::RefCell;
use std::fmt::{self, Debug, Formatter};
use std::path::Path;
use std::rc::Rc;
use crate::error::Result;
use crate::graphics::text::cache::{FontCache, TextGeometry};
use crate::graphics::{self, DrawParams, Rectangle};
use crate::Context;
#[cfg(feature = "font_ttf")]
pub use crate::graphics::text::vector::VectorFontBuilder;
pub use crate::graphics::text::bmfont::BmFontBuilder;
use super::FilterMode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum FontTextureStyle {
Normal,
Premultiplied,
}
#[derive(Clone)]
pub struct Font {
data: Rc<RefCell<FontCache>>,
}
impl Font {
#[cfg(feature = "font_ttf")]
pub fn vector<P>(ctx: &mut Context, path: P, size: f32) -> Result<Font>
where
P: AsRef<Path>,
{
VectorFontBuilder::new(path)?.with_size(ctx, size)
}
#[cfg(feature = "font_ttf")]
pub fn from_vector_file_data(
ctx: &mut Context,
data: &'static [u8],
size: f32,
) -> Result<Font> {
VectorFontBuilder::from_file_data(data)?.with_size(ctx, size)
}
pub fn bmfont<P>(ctx: &mut Context, path: P) -> Result<Font>
where
P: AsRef<Path>,
{
BmFontBuilder::new(path)?.build(ctx)
}
pub fn filter_mode(&self) -> FilterMode {
self.data.borrow().filter_mode()
}
pub fn set_filter_mode(&mut self, ctx: &mut Context, filter_mode: FilterMode) {
self.data.borrow_mut().set_filter_mode(ctx, filter_mode);
}
}
impl Debug for Font {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Font").finish()
}
}
#[derive(Debug, Clone)]
pub struct Text {
content: String,
font: Font,
max_width: Option<f32>,
geometry: Option<TextGeometry>,
}
impl Text {
pub fn new<C>(content: C, font: Font) -> Text
where
C: Into<String>,
{
Text {
content: content.into(),
font,
max_width: None,
geometry: None,
}
}
pub fn wrapped<C>(content: C, font: Font, max_width: f32) -> Text
where
C: Into<String>,
{
Text {
content: content.into(),
font,
max_width: Some(max_width),
geometry: None,
}
}
pub fn draw<P>(&mut self, ctx: &mut Context, params: P)
where
P: Into<DrawParams>,
{
self.update_geometry(ctx);
let params = params.into();
let data = self.font.data.borrow();
let texture = data.texture();
let geometry = self
.geometry
.as_ref()
.expect("geometry should have been generated");
graphics::set_texture(ctx, texture);
let (texture_width, texture_height) = texture.size();
for quad in &geometry.quads {
graphics::push_quad(
ctx,
quad.position.x,
quad.position.y,
quad.position.x + quad.region.width,
quad.position.y + quad.region.height,
quad.region.x / (texture_width as f32),
quad.region.y / (texture_height as f32),
quad.region.right() / (texture_width as f32),
quad.region.bottom() / (texture_height as f32),
¶ms,
);
}
}
pub fn content(&self) -> &str {
&self.content
}
pub fn set_content<C>(&mut self, content: C)
where
C: Into<String>,
{
self.geometry.take();
self.content = content.into();
}
pub fn font(&self) -> &Font {
&self.font
}
pub fn set_font(&mut self, font: Font) {
self.geometry.take();
self.font = font;
}
pub fn max_width(&self) -> Option<f32> {
self.max_width
}
pub fn set_max_width(&mut self, max_width: Option<f32>) {
self.geometry.take();
self.max_width = max_width;
}
pub fn push(&mut self, ch: char) {
self.geometry.take();
self.content.push(ch);
}
pub fn push_str(&mut self, string: &str) {
self.geometry.take();
self.content.push_str(string);
}
pub fn pop(&mut self) -> Option<char> {
self.geometry.take();
self.content.pop()
}
pub fn get_bounds(&mut self, ctx: &mut Context) -> Option<Rectangle> {
self.update_geometry(ctx);
self.geometry
.as_ref()
.expect("geometry should have been generated")
.bounds
}
fn update_geometry(&mut self, ctx: &mut Context) {
let mut data = self.font.data.borrow_mut();
let needs_render = match &self.geometry {
None => true,
Some(g) => g.resize_count != data.resize_count(),
};
if needs_render {
let new_geometry = data.render(&mut ctx.device, &self.content, self.max_width);
self.geometry = Some(new_geometry);
}
}
}