move ownership of GlyphCache into Canvas, so calculating text dimensions can be done during event. Stick the glyphs behind a RefCell to avoid mutability creeping into canvas everywhere

This commit is contained in:
Dustin Carlino 2018-12-16 14:56:28 -08:00
parent 671dad6b02
commit 62bdd92ac3
6 changed files with 50 additions and 41 deletions

View File

@ -3,7 +3,9 @@
use crate::{text, GfxCtx, Text, UserInput};
use geom::{Bounds, Pt2D};
use graphics::Transformed;
use opengl_graphics::{Filter, GlyphCache, TextureSettings};
use piston::window::Size;
use std::cell::RefCell;
const ZOOM_SPEED: f64 = 0.1;
@ -20,10 +22,24 @@ pub struct Canvas {
left_mouse_drag_from: Option<(f64, f64)>,
pub window_size: Size,
glyphs: RefCell<GlyphCache<'static>>,
}
impl Canvas {
pub fn new() -> Canvas {
let texture_settings = TextureSettings::new().filter(Filter::Nearest);
// TODO We could also preload everything and not need the RefCell.
let glyphs = RefCell::new(
GlyphCache::new(
// TODO don't assume this exists!
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
(),
texture_settings,
)
.expect("Could not load font"),
);
Canvas {
cam_x: 0.0,
cam_y: 0.0,
@ -37,6 +53,8 @@ impl Canvas {
width: 0,
height: 0,
},
glyphs,
}
}
@ -77,20 +95,22 @@ impl Canvas {
}
pub fn draw_mouse_tooltip(&self, g: &mut GfxCtx, txt: Text) {
let (width, height) = txt.dims(g);
let glyphs = &mut self.glyphs.borrow_mut();
let (width, height) = txt.dims(glyphs);
let x1 = self.cursor_x - (width / 2.0);
let y1 = self.cursor_y - (height / 2.0);
text::draw_text_bubble(g, (x1, y1), txt);
text::draw_text_bubble(g, glyphs, (x1, y1), txt);
}
pub fn draw_text_at(&self, g: &mut GfxCtx, txt: Text, pt: Pt2D) {
let (width, height) = txt.dims(g);
let glyphs = &mut self.glyphs.borrow_mut();
let (width, height) = txt.dims(glyphs);
let (x, y) = self.map_to_screen(pt);
text::draw_text_bubble(g, (x - (width / 2.0), y - (height / 2.0)), txt);
text::draw_text_bubble(g, glyphs, (x - (width / 2.0), y - (height / 2.0)), txt);
}
pub fn draw_text_at_screenspace_topleft(&self, g: &mut GfxCtx, txt: Text, (x, y): (f64, f64)) {
text::draw_text_bubble(g, (x, y), txt);
text::draw_text_bubble(g, &mut self.glyphs.borrow_mut(), (x, y), txt);
}
pub fn draw_text(
@ -102,7 +122,8 @@ impl Canvas {
if txt.is_empty() {
return;
}
let (width, height) = txt.dims(g);
let glyphs = &mut self.glyphs.borrow_mut();
let (width, height) = txt.dims(glyphs);
let x1 = match horiz {
HorizontalAlignment::Left => 0.0,
HorizontalAlignment::Center => (f64::from(self.window_size.width) - width) / 2.0,
@ -113,7 +134,11 @@ impl Canvas {
VerticalAlignment::Center => (f64::from(self.window_size.height) - height) / 2.0,
VerticalAlignment::Bottom => f64::from(self.window_size.height) - height,
};
text::draw_text_bubble(g, (x1, y1), txt);
text::draw_text_bubble(g, glyphs, (x1, y1), txt);
}
pub(crate) fn text_dims(&self, txt: &Text) -> (f64, f64) {
txt.dims(&mut self.glyphs.borrow_mut())
}
fn zoom_towards_mouse(&mut self, delta_zoom: f64) {

View File

@ -343,7 +343,7 @@ pub(crate) struct ContextMenu {
}
impl ContextMenu {
pub(crate) fn calculate_geometry(&mut self, g: &mut GfxCtx, canvas: &Canvas) {
pub(crate) fn calculate_geometry(&mut self, canvas: &mut Canvas) {
if self.geometry.is_some() {
return;
}
@ -352,7 +352,7 @@ impl ContextMenu {
for (hotkey, action) in &self.actions {
txt.add_line(format!("{} - {}", hotkey.describe(), action));
}
let (screen_width, screen_height) = txt.dims(g);
let (screen_width, screen_height) = canvas.text_dims(&txt);
let map_width = screen_width / canvas.cam_zoom;
let map_height = screen_height / canvas.cam_zoom;
let top_left = Pt2D::new(

View File

@ -25,27 +25,19 @@ pub use crate::text::{Text, TEXT_FG_COLOR};
pub use crate::text_box::TextBox;
pub use crate::wizard::{Wizard, WrappedWizard};
use geom::Pt2D;
use graphics::character::CharacterCache;
use graphics::Transformed;
use opengl_graphics::{GlGraphics, Texture};
use opengl_graphics::GlGraphics;
use std::mem;
//struct GfxCtx<'a, G: 'a + Graphics, C: 'a + CharacterCache<Texture = G::Texture>> {
pub struct GfxCtx<'a> {
glyphs: &'a mut CharacterCache<Texture = Texture, Error = String>,
orig_ctx: graphics::Context,
ctx: graphics::Context,
gfx: &'a mut GlGraphics,
}
impl<'a> GfxCtx<'a> {
pub fn new(
glyphs: &'a mut CharacterCache<Texture = Texture, Error = String>,
g: &'a mut GlGraphics,
c: graphics::Context,
) -> GfxCtx<'a> {
pub fn new(g: &'a mut GlGraphics, c: graphics::Context) -> GfxCtx<'a> {
GfxCtx {
glyphs,
gfx: g,
orig_ctx: c,
ctx: c,

View File

@ -87,7 +87,7 @@ impl<T: Clone> Menu<T> {
InputResult::StillActive
}
pub(crate) fn calculate_geometry(&mut self, g: &mut GfxCtx, canvas: &Canvas) {
pub(crate) fn calculate_geometry(&mut self, canvas: &mut Canvas) {
if self.geometry.is_some() {
return;
}
@ -97,7 +97,7 @@ impl<T: Clone> Menu<T> {
for (hotkey, choice, _) in &self.choices {
txt.add_line(format!("{} - {}", hotkey.describe(), choice));
}
let (screen_width, screen_height) = txt.dims(g);
let (screen_width, screen_height) = canvas.text_dims(&txt);
let map_width = screen_width / canvas.cam_zoom;
let map_height = screen_height / canvas.cam_zoom;
let top_left = Pt2D::new(

View File

@ -1,7 +1,7 @@
use crate::input::ContextMenu;
use crate::{Canvas, Event, GfxCtx, UserInput};
use glutin_window::GlutinWindow;
use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventLoop, EventSettings, Events};
use piston::window::{Window, WindowSettings};
use std::panic;
@ -32,15 +32,6 @@ pub fn run<T, G: GUI<T>>(mut gui: G, window_title: &str, initial_width: u32, ini
let mut events = Events::new(EventSettings::new().lazy(true));
let mut gl = GlGraphics::new(opengl);
let texture_settings = TextureSettings::new().filter(Filter::Nearest);
let mut glyphs = GlyphCache::new(
// TODO don't assume this exists!
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
(),
texture_settings,
)
.expect("Could not load font");
let mut last_event_mode = EventLoopMode::InputOnly;
let mut context_menu: Option<ContextMenu> = None;
let mut last_data: Option<T> = None;
@ -50,7 +41,7 @@ pub fn run<T, G: GUI<T>>(mut gui: G, window_title: &str, initial_width: u32, ini
// If the very first event is render, then just wait.
if let Some(ref data) = last_data {
gl.draw(args.viewport(), |c, g| {
let mut g = GfxCtx::new(&mut glyphs, g, c);
let mut g = GfxCtx::new(g, c);
gui.get_mut_canvas()
.start_drawing(&mut g, window.draw_size());
@ -63,9 +54,8 @@ pub fn run<T, G: GUI<T>>(mut gui: G, window_title: &str, initial_width: u32, ini
// Always draw the context-menu last.
if let Some(ref mut menu) = context_menu {
// TODO Weird to do this here (all to pass along &mut glyphs), but for the
// moment, that's how Text::dims works. :\
menu.calculate_geometry(&mut g, gui.get_mut_canvas());
// TODO Can get rid of this weird method now!
menu.calculate_geometry(gui.get_mut_canvas());
menu.draw(&mut g, gui.get_mut_canvas());
}
});

View File

@ -1,7 +1,9 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use crate::{Canvas, Color, GfxCtx};
use graphics::character::CharacterCache;
use graphics::{Image, Rectangle, Transformed};
use opengl_graphics::GlyphCache;
use textwrap;
pub const TEXT_FG_COLOR: Color = Color([0.0, 0.0, 0.0, 1.0]);
@ -98,7 +100,7 @@ impl Text {
self.lines.is_empty()
}
pub fn dims(&self, g: &mut GfxCtx) -> (f64, f64) {
pub(crate) fn dims(&self, glyphs: &mut GlyphCache) -> (f64, f64) {
let longest_line = self
.lines
.iter()
@ -108,14 +110,14 @@ impl Text {
for span in longest_line {
concat.push_str(&span.text);
}
let width = g.glyphs.width(FONT_SIZE, &concat).unwrap();
let width = glyphs.width(FONT_SIZE, &concat).unwrap();
let height = (self.lines.len() as f64) * LINE_HEIGHT;
(width, height)
}
}
pub fn draw_text_bubble(g: &mut GfxCtx, (x1, y1): (f64, f64), txt: Text) {
let (total_width, total_height) = txt.dims(g);
pub fn draw_text_bubble(g: &mut GfxCtx, glyphs: &mut GlyphCache, (x1, y1): (f64, f64), txt: Text) {
let (total_width, total_height) = txt.dims(glyphs);
Rectangle::new(txt.bg_color.0).draw(
[x1, y1, total_width, total_height],
&g.orig_ctx.draw_state,
@ -130,7 +132,7 @@ pub fn draw_text_bubble(g: &mut GfxCtx, (x1, y1): (f64, f64), txt: Text) {
for span in line {
if let Some(color) = span.highlight_color {
// TODO do we ever want to use total_width?
let width = g.glyphs.width(FONT_SIZE, &span.text).unwrap();
let width = glyphs.width(FONT_SIZE, &span.text).unwrap();
Rectangle::new(color.0).draw(
[x, y - LINE_HEIGHT, width, LINE_HEIGHT],
&g.orig_ctx.draw_state,
@ -142,7 +144,7 @@ pub fn draw_text_bubble(g: &mut GfxCtx, (x1, y1): (f64, f64), txt: Text) {
let fg_text = Image::new_color(span.fg_color.0);
for ch in span.text.chars() {
if let Ok(draw_ch) = g.glyphs.character(FONT_SIZE, ch) {
if let Ok(draw_ch) = glyphs.character(FONT_SIZE, ch) {
let char_ctx = g
.orig_ctx
.transform