201 lines
5.3 KiB
Rust
201 lines
5.3 KiB
Rust
use macroquad::color::{BLACK, Color};
|
|
use macroquad::math::Vec2;
|
|
use macroquad::prelude::{Font, TextDimensions};
|
|
use macroquad::text::{TextParams, draw_text_ex, measure_text};
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
pub enum Justified {
|
|
Right,
|
|
Left,
|
|
#[default]
|
|
Center,
|
|
CenterVertical,
|
|
CenterHorizontal,
|
|
}
|
|
|
|
impl Justified {
|
|
pub fn text_offset(&self, text_size: TextDimensions) -> Vec2 {
|
|
match self {
|
|
Justified::Right => Vec2::new(text_size.width, 0.0) * 0.5,
|
|
Justified::Left => Vec2::new(0.0, 0.0),
|
|
Justified::Center => -Vec2::new(text_size.width, text_size.height) * 0.5,
|
|
Justified::CenterVertical => -Vec2::new(0.0, text_size.height) * 0.5,
|
|
Justified::CenterHorizontal => -Vec2::new(text_size.width, 0.0) * 0.5,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone)]
|
|
pub struct TextConfigBuilder<'a> {
|
|
text_config: TextConfig<'a>,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl<'a> TextConfigBuilder<'a> {
|
|
pub fn new(text_config: TextConfig<'a>) -> Self {
|
|
Self { text_config }
|
|
}
|
|
|
|
pub fn font(mut self, font: &'a Font) -> Self {
|
|
self.text_config.font = Some(font);
|
|
self
|
|
}
|
|
|
|
pub fn font_size(mut self, font_size: u16) -> Self {
|
|
self.text_config.font_size = font_size;
|
|
self
|
|
}
|
|
|
|
pub fn font_scale(mut self, font_scale: f32) -> Self {
|
|
self.text_config.font_scale = font_scale;
|
|
self
|
|
}
|
|
|
|
pub fn font_scale_aspect(mut self, font_scale_aspect: f32) -> Self {
|
|
self.text_config.font_scale_aspect = font_scale_aspect;
|
|
self
|
|
}
|
|
|
|
pub fn rotation(mut self, rotation: f32) -> Self {
|
|
self.text_config.rotation = rotation;
|
|
self
|
|
}
|
|
|
|
pub fn color(mut self, color: Color) -> Self {
|
|
self.text_config.color = color;
|
|
self
|
|
}
|
|
|
|
pub fn justification(mut self, justification: Justified) -> Self {
|
|
self.text_config.justification = justification;
|
|
self
|
|
}
|
|
|
|
pub fn build(self) -> TextConfig<'a> {
|
|
self.text_config
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TextInfo {
|
|
pub text_dimensions: TextDimensions,
|
|
pub pos: Vec2,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TextConfig<'a> {
|
|
font: Option<&'a Font>,
|
|
font_size: u16,
|
|
font_scale: f32,
|
|
font_scale_aspect: f32,
|
|
rotation: f32,
|
|
color: Color,
|
|
justification: Justified,
|
|
}
|
|
|
|
impl<'a> Default for TextConfig<'a> {
|
|
fn default() -> Self {
|
|
Self {
|
|
font: None,
|
|
font_size: 12,
|
|
font_scale: 1.0,
|
|
font_scale_aspect: 1.0,
|
|
rotation: 0.0,
|
|
color: BLACK,
|
|
justification: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> TextConfig<'a> {
|
|
pub fn draw_text(&self, text: &str, pos: Vec2) -> TextInfo {
|
|
let text_size = self.measure_text(text);
|
|
let text_params = TextParams {
|
|
font: self.font,
|
|
font_size: self.font_size,
|
|
font_scale: self.font_scale,
|
|
font_scale_aspect: self.font_scale_aspect,
|
|
rotation: self.rotation,
|
|
color: self.color,
|
|
};
|
|
|
|
let text_pos = pos + self.justification.text_offset(text_size);
|
|
|
|
draw_text_ex(text, text_pos.x, text_pos.y, text_params);
|
|
|
|
TextInfo {
|
|
text_dimensions: text_size,
|
|
pos,
|
|
}
|
|
}
|
|
|
|
pub fn draw_text_box(&self, text: &str, pos: Vec2, size: Vec2) -> TextInfo {
|
|
let mut lines: Vec<String> = text.split("\n").map(|s| s.to_string()).collect();
|
|
let mut line_pos = pos;
|
|
|
|
let standard_line_height = self.measure_text("A").height;
|
|
|
|
let mut line_ndx = 0;
|
|
loop {
|
|
if line_ndx >= lines.len() {
|
|
break;
|
|
}
|
|
|
|
let line = &lines[line_ndx];
|
|
let line: Vec<&str> = line.split(" ").collect();
|
|
|
|
let mut word_pos = line_pos;
|
|
|
|
let mut need_line_break = false;
|
|
for word_ndx in 0..line.len() {
|
|
let word = line[word_ndx];
|
|
|
|
let measure_text = self.measure_text(&format!("{word} "));
|
|
|
|
if measure_text.width + word_pos.x > size.x {
|
|
let new_line = format!("\t\t\t{}", line[word_ndx..].join(" "));
|
|
lines.insert(line_ndx + 1, new_line);
|
|
need_line_break = true;
|
|
break;
|
|
}
|
|
|
|
let word_info = self.draw_text(&format!("{word} "), word_pos);
|
|
word_pos.x += word_info.text_dimensions.width;
|
|
}
|
|
|
|
line_ndx += 1;
|
|
|
|
if need_line_break {
|
|
line_pos.y += standard_line_height * 1.5;
|
|
} else {
|
|
line_pos.y += standard_line_height * 2.0;
|
|
}
|
|
|
|
if line_pos.y > size.y {
|
|
// Stop drawing text
|
|
break;
|
|
}
|
|
}
|
|
|
|
TextInfo {
|
|
text_dimensions: TextDimensions {
|
|
width: size.x,
|
|
height: size.y,
|
|
offset_y: 0.0,
|
|
},
|
|
pos,
|
|
}
|
|
}
|
|
|
|
pub fn measure_text(&self, text: &str) -> TextDimensions {
|
|
measure_text(text, self.font, self.font_size, self.font_scale)
|
|
}
|
|
}
|
|
|
|
impl<'a> From<TextConfig<'a>> for TextConfigBuilder<'a> {
|
|
fn from(val: TextConfig<'a>) -> Self {
|
|
TextConfigBuilder::new(val)
|
|
}
|
|
}
|