use super::*;
pub(crate) mod escape;
mod font;
mod resource;
mod v;
pub use self::font::IFont;
pub use self::resource::FontResource;
pub use self::v::*;
pub type TextBuffer = DrawBuilder<TextVertex, TextUniform>;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum TextAlign {
TopLeft = 0,
TopCenter = 1,
TopRight = 2,
MiddleLeft = 4,
MiddleCenter = 5,
MiddleRight = 6,
BottomLeft = 8,
BottomCenter = 9,
BottomRight = 10,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Scribe {
pub font_size: f32,
pub font_width_scale: f32,
pub line_height: f32,
pub baseline: f32,
pub x_pos: f32,
pub letter_spacing: f32,
pub top_skew: f32,
pub color: Vec4<u8>,
pub outline: Vec4<u8>,
pub draw_mask: bool,
}
impl Default for Scribe {
#[inline]
fn default() -> Self {
Scribe {
font_size: 16.0,
font_width_scale: 1.0,
line_height: 16.0,
baseline: 0.0,
x_pos: 0.0,
letter_spacing: 0.0,
top_skew: 0.0,
color: Vec4(255, 255, 255, 255),
outline: Vec4(0, 0, 0, 255),
draw_mask: true,
}
}
}
impl Scribe {
#[inline]
pub fn set_baseline_relative(&mut self, fraction: f32) {
self.baseline = (self.line_height - self.font_size) * fraction;
}
#[inline]
pub fn text_width(&self, cursor: &mut Vec2f, font: &dyn IFont, text: &str) -> f32 {
let mut scribe_st = self.clone();
let mut width = 0.0;
let mut x_pos = cursor.x;
for line in text.lines() {
font.write_span(None, &mut scribe_st, cursor, line);
width = f32::max(width, cursor.x - x_pos);
x_pos = scribe_st.x_pos;
cursor.x = scribe_st.x_pos;
cursor.y += scribe_st.line_height;
}
return width;
}
#[inline(never)]
pub fn text_height(&self, text: &str) -> f32 {
text.lines().count() as i32 as f32 * self.line_height
}
}
impl TextBuffer {
pub fn text_write<T: fmt::Display>(&mut self, font: &FontResource<impl IFont>, scribe: &mut Scribe, cursor: &mut Vec2f, text: T) {
self.uniform.texture = font.texture;
self.shader = font.shader;
text_write(self, font.as_dyn().font, scribe, cursor, &text);
}
pub fn text_lines(&mut self, font: &FontResource<impl IFont>, scribe: &Scribe, rect: &Bounds2f, align: TextAlign, lines: &[&dyn fmt::Display]) {
self.uniform.texture = font.texture;
self.shader = font.shader;
text_lines(self, font.as_dyn().font, scribe, rect, align, lines);
}
pub fn text_box(&mut self, font: &FontResource<impl IFont>, scribe: &Scribe, rect: &Bounds2f, align: TextAlign, text: &str) {
self.uniform.texture = font.texture;
self.shader = font.shader;
text_box(self, font.as_dyn().font, scribe, rect, align, text);
}
}
#[repr(transparent)]
struct FormatFn<F>(F);
impl<F: FnMut(&str) -> fmt::Result> fmt::Write for FormatFn<F> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0(s)
}
}
fn text_write(buf: &mut TextBuffer, font: &dyn IFont, scribe: &mut Scribe, cursor: &mut Vec2f, text: &dyn fmt::Display) {
let mut writer = FormatFn(move |text: &str| {
font.write_span(Some(buf), scribe, cursor, text);
Ok(())
});
let _ = fmt::write(&mut writer, format_args!("{}", text));
}
fn text_lines(buf: &mut TextBuffer, font: &dyn IFont, scribe: &Scribe, rect: &Bounds2f, align: TextAlign, lines: &[&dyn fmt::Display]) {
let height = lines.len() as isize as i32 as f32 * scribe.line_height;
let mut y = match align {
TextAlign::TopLeft | TextAlign::TopCenter | TextAlign::TopRight => rect.mins.y,
TextAlign::MiddleLeft | TextAlign::MiddleCenter | TextAlign::MiddleRight => rect.mins.y + (rect.height() - height) * 0.5,
TextAlign::BottomLeft | TextAlign::BottomCenter | TextAlign::BottomRight => rect.maxs.y - height,
};
let mut scribe = scribe.clone();
for &args in lines {
let text_width = |args| {
let mut width = 0.0;
let mut width_calc = FormatFn(|text: &str| {
width += scribe.text_width(&mut {Vec2::ZERO}, font, text);
Ok(())
});
let _ = fmt::write(&mut width_calc, format_args!("{}", args));
width
};
let x = match align {
TextAlign::TopLeft | TextAlign::MiddleLeft | TextAlign::BottomLeft => rect.mins.x,
TextAlign::TopCenter | TextAlign::MiddleCenter | TextAlign::BottomCenter => rect.mins.x + (rect.width() - text_width(args)) * 0.5,
TextAlign::TopRight | TextAlign::MiddleRight | TextAlign::BottomRight => rect.maxs.x - text_width(args),
};
let mut cursor = Vec2(x, y);
let mut writer = FormatFn(|text: &str| {
font.write_span(Some(buf), &mut scribe, &mut cursor, text);
Ok(())
});
let _ = fmt::write(&mut writer, format_args!("{}", args));
y += scribe.line_height;
}
}
fn text_box(buf: &mut TextBuffer, font: &dyn IFont, scribe: &Scribe, rect: &Bounds2f, align: TextAlign, text: &str) {
let mut y = match align {
TextAlign::TopLeft | TextAlign::TopCenter | TextAlign::TopRight => rect.mins.y,
TextAlign::MiddleLeft | TextAlign::MiddleCenter | TextAlign::MiddleRight => rect.mins.y + (rect.height() - scribe.text_height(text)) * 0.5,
TextAlign::BottomLeft | TextAlign::BottomCenter | TextAlign::BottomRight => rect.maxs.y - scribe.text_height(text),
};
let mut scribe = scribe.clone();
for line in text.lines() {
let x = match align {
TextAlign::TopLeft | TextAlign::MiddleLeft | TextAlign::BottomLeft => rect.mins.x,
TextAlign::TopCenter | TextAlign::MiddleCenter | TextAlign::BottomCenter => rect.mins.x + (rect.width() - scribe.text_width(&mut {Vec2::ZERO}, font, line)) * 0.5,
TextAlign::TopRight | TextAlign::MiddleRight | TextAlign::BottomRight => rect.maxs.x - scribe.text_width(&mut {Vec2::ZERO}, font, line),
};
font.write_span(Some(buf), &mut scribe, &mut Vec2(x, y), line);
y += scribe.line_height;
}
}