use ggez::glam::Vec2;
use ggez::graphics::{self, Color, PxScale, Text, TextAlign, TextFragment};
use ggez::timer;
use ggez::{
conf::{WindowMode, WindowSetup},
graphics::Drawable,
};
use ggez::{event, graphics::TextLayout};
use ggez::{Context, ContextBuilder, GameResult};
use std::collections::BTreeMap;
use std::env;
use std::path;
fn random_color(rng: &mut oorandom::Rand32) -> Color {
Color::new(rng.rand_float(), rng.rand_float(), rng.rand_float(), 1.0)
}
struct App {
texts: BTreeMap<&'static str, Text>,
rng: oorandom::Rand32,
}
impl App {
#[allow(clippy::needless_update)]
fn new(ctx: &mut Context) -> GameResult<App> {
let mut texts = BTreeMap::new();
let mut rng = oorandom::Rand32::new(314159);
let text = Text::new("Hello, World!");
texts.insert("0_hello", text);
let mut text = Text::new(TextFragment {
text: "Small red fragment".to_string(),
color: Some(Color::new(1.0, 0.0, 0.0, 1.0)),
font: Some("LiberationMono-Regular".into()),
scale: Some(PxScale::from(10.0)),
..Default::default()
});
text.add(" default fragment, should be long enough to showcase everything")
.add(TextFragment::new(" magenta fragment").color(Color::new(1.0, 0.0, 1.0, 1.0)))
.add(" another default fragment, to really drive the point home");
ctx.gfx.add_font(
"Fancy font",
graphics::FontData::from_path(ctx, "/Tangerine_Regular.ttf")?,
);
text.add(
TextFragment::new(" fancy fragment")
.font("Fancy font")
.scale(PxScale::from(25.0)),
)
.add(" and a default one, for symmetry");
texts.insert("1_demo_text_1", text.clone());
text.set_bounds(Vec2::new(400.0, f32::INFINITY))
.set_layout(TextLayout {
h_align: TextAlign::Begin,
v_align: TextAlign::Begin,
});
texts.insert("1_demo_text_2", text.clone());
text.set_bounds(Vec2::new(500.0, f32::INFINITY))
.set_layout(TextLayout {
h_align: TextAlign::End,
v_align: TextAlign::Begin,
});
texts.insert("1_demo_text_3", text.clone());
text.set_font("Fancy font")
.set_scale(16.0)
.set_bounds(Vec2::new(300.0, f32::INFINITY))
.set_layout(TextLayout {
h_align: TextAlign::Middle,
v_align: TextAlign::Begin,
});
texts.insert("1_demo_text_4", text);
let chroma_string = "Not quite a rainbow.";
let mut chroma_text = Text::default();
for ch in chroma_string.chars() {
chroma_text.add(TextFragment::new(ch).color(random_color(&mut rng)));
}
texts.insert("2_rainbow", chroma_text);
let wonky_string = "So, so wonky.";
let mut wonky_text = Text::default();
for ch in wonky_string.chars() {
wonky_text
.add(TextFragment::new(ch).scale(PxScale::from(10.0 + 24.0 * rng.rand_float())));
}
texts.insert("3_wonky", wonky_text);
Ok(App { texts, rng })
}
}
impl event::EventHandler<ggez::GameError> for App {
fn update(&mut self, ctx: &mut Context) -> GameResult {
const DESIRED_FPS: u32 = 60;
while ctx.time.check_update_time(DESIRED_FPS) {}
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult {
let mut canvas = graphics::Canvas::from_frame(ctx, Color::from([0.1, 0.2, 0.3, 1.0]));
let fps = ctx.time.fps();
let fps_display = Text::new(format!("FPS: {}", fps));
canvas.draw(
&fps_display,
graphics::DrawParam::from([200.0, 0.0]).color(Color::WHITE),
);
let mut height = 0.0;
for (key, text) in &self.texts {
let x = match *key {
"1_demo_text_3" => 500.0 + 20.0,
"1_demo_text_4" => (300.0 / 2.0) + 20.0,
_ => 20.0,
};
canvas.draw(text, Vec2::new(x, 20.0 + height));
height += 20.0 + text.dimensions(ctx).unwrap().h
}
if let Some(text) = self.texts.get_mut("1_demo_text_3") {
text.fragments_mut()[3].color = Some(random_color(&mut self.rng));
}
let wobble_string = "WOBBLE";
let mut wobble = Text::default();
for ch in wobble_string.chars() {
wobble.add(
TextFragment::new(ch).scale(PxScale::from(10.0 + 6.0 * self.rng.rand_float())),
);
}
let wobble_rect = wobble.dimensions(ctx).unwrap();
canvas.draw(
&wobble,
graphics::DrawParam::new()
.color((0.0, 1.0, 1.0, 1.0))
.dest([500.0, 300.0])
.rotation(-0.5),
);
let t = Text::new(format!(
"width: {}\nheight: {}",
wobble_rect.w, wobble_rect.h
));
canvas.draw(&t, graphics::DrawParam::from([500.0, 320.0]).rotation(-0.5));
canvas.finish(ctx)?;
timer::yield_now();
Ok(())
}
}
pub fn main() -> GameResult {
if cfg!(debug_assertions) && env::var("yes_i_really_want_debug_mode").is_err() {
eprintln!(
"Note: Release mode will improve performance greatly.\n \
e.g. use `cargo run --example text --release`"
);
}
let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
let mut path = path::PathBuf::from(manifest_dir);
path.push("resources");
path
} else {
path::PathBuf::from("./resources")
};
let (mut ctx, events_loop) = ContextBuilder::new("text_cached", "ggez")
.window_setup(WindowSetup::default().title("Cached text example!"))
.window_mode(
WindowMode::default()
.dimensions(640.0, 480.0)
.resizable(true),
)
.add_resource_path(resource_dir)
.build()?;
let state = App::new(&mut ctx)?;
event::run(ctx, events_loop, state)
}