mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-28 08:53:26 +03:00
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:
parent
671dad6b02
commit
62bdd92ac3
@ -3,7 +3,9 @@
|
|||||||
use crate::{text, GfxCtx, Text, UserInput};
|
use crate::{text, GfxCtx, Text, UserInput};
|
||||||
use geom::{Bounds, Pt2D};
|
use geom::{Bounds, Pt2D};
|
||||||
use graphics::Transformed;
|
use graphics::Transformed;
|
||||||
|
use opengl_graphics::{Filter, GlyphCache, TextureSettings};
|
||||||
use piston::window::Size;
|
use piston::window::Size;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
const ZOOM_SPEED: f64 = 0.1;
|
const ZOOM_SPEED: f64 = 0.1;
|
||||||
|
|
||||||
@ -20,10 +22,24 @@ pub struct Canvas {
|
|||||||
left_mouse_drag_from: Option<(f64, f64)>,
|
left_mouse_drag_from: Option<(f64, f64)>,
|
||||||
|
|
||||||
pub window_size: Size,
|
pub window_size: Size,
|
||||||
|
|
||||||
|
glyphs: RefCell<GlyphCache<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Canvas {
|
impl Canvas {
|
||||||
pub fn new() -> 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 {
|
Canvas {
|
||||||
cam_x: 0.0,
|
cam_x: 0.0,
|
||||||
cam_y: 0.0,
|
cam_y: 0.0,
|
||||||
@ -37,6 +53,8 @@ impl Canvas {
|
|||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
glyphs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,20 +95,22 @@ impl Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_mouse_tooltip(&self, g: &mut GfxCtx, txt: Text) {
|
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 x1 = self.cursor_x - (width / 2.0);
|
||||||
let y1 = self.cursor_y - (height / 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) {
|
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);
|
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)) {
|
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(
|
pub fn draw_text(
|
||||||
@ -102,7 +122,8 @@ impl Canvas {
|
|||||||
if txt.is_empty() {
|
if txt.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (width, height) = txt.dims(g);
|
let glyphs = &mut self.glyphs.borrow_mut();
|
||||||
|
let (width, height) = txt.dims(glyphs);
|
||||||
let x1 = match horiz {
|
let x1 = match horiz {
|
||||||
HorizontalAlignment::Left => 0.0,
|
HorizontalAlignment::Left => 0.0,
|
||||||
HorizontalAlignment::Center => (f64::from(self.window_size.width) - width) / 2.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::Center => (f64::from(self.window_size.height) - height) / 2.0,
|
||||||
VerticalAlignment::Bottom => f64::from(self.window_size.height) - height,
|
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) {
|
fn zoom_towards_mouse(&mut self, delta_zoom: f64) {
|
||||||
|
@ -343,7 +343,7 @@ pub(crate) struct ContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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() {
|
if self.geometry.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -352,7 +352,7 @@ impl ContextMenu {
|
|||||||
for (hotkey, action) in &self.actions {
|
for (hotkey, action) in &self.actions {
|
||||||
txt.add_line(format!("{} - {}", hotkey.describe(), action));
|
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_width = screen_width / canvas.cam_zoom;
|
||||||
let map_height = screen_height / canvas.cam_zoom;
|
let map_height = screen_height / canvas.cam_zoom;
|
||||||
let top_left = Pt2D::new(
|
let top_left = Pt2D::new(
|
||||||
|
@ -25,27 +25,19 @@ pub use crate::text::{Text, TEXT_FG_COLOR};
|
|||||||
pub use crate::text_box::TextBox;
|
pub use crate::text_box::TextBox;
|
||||||
pub use crate::wizard::{Wizard, WrappedWizard};
|
pub use crate::wizard::{Wizard, WrappedWizard};
|
||||||
use geom::Pt2D;
|
use geom::Pt2D;
|
||||||
use graphics::character::CharacterCache;
|
|
||||||
use graphics::Transformed;
|
use graphics::Transformed;
|
||||||
use opengl_graphics::{GlGraphics, Texture};
|
use opengl_graphics::GlGraphics;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
//struct GfxCtx<'a, G: 'a + Graphics, C: 'a + CharacterCache<Texture = G::Texture>> {
|
|
||||||
pub struct GfxCtx<'a> {
|
pub struct GfxCtx<'a> {
|
||||||
glyphs: &'a mut CharacterCache<Texture = Texture, Error = String>,
|
|
||||||
orig_ctx: graphics::Context,
|
orig_ctx: graphics::Context,
|
||||||
ctx: graphics::Context,
|
ctx: graphics::Context,
|
||||||
gfx: &'a mut GlGraphics,
|
gfx: &'a mut GlGraphics,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GfxCtx<'a> {
|
impl<'a> GfxCtx<'a> {
|
||||||
pub fn new(
|
pub fn new(g: &'a mut GlGraphics, c: graphics::Context) -> GfxCtx<'a> {
|
||||||
glyphs: &'a mut CharacterCache<Texture = Texture, Error = String>,
|
|
||||||
g: &'a mut GlGraphics,
|
|
||||||
c: graphics::Context,
|
|
||||||
) -> GfxCtx<'a> {
|
|
||||||
GfxCtx {
|
GfxCtx {
|
||||||
glyphs,
|
|
||||||
gfx: g,
|
gfx: g,
|
||||||
orig_ctx: c,
|
orig_ctx: c,
|
||||||
ctx: c,
|
ctx: c,
|
||||||
|
@ -87,7 +87,7 @@ impl<T: Clone> Menu<T> {
|
|||||||
InputResult::StillActive
|
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() {
|
if self.geometry.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ impl<T: Clone> Menu<T> {
|
|||||||
for (hotkey, choice, _) in &self.choices {
|
for (hotkey, choice, _) in &self.choices {
|
||||||
txt.add_line(format!("{} - {}", hotkey.describe(), choice));
|
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_width = screen_width / canvas.cam_zoom;
|
||||||
let map_height = screen_height / canvas.cam_zoom;
|
let map_height = screen_height / canvas.cam_zoom;
|
||||||
let top_left = Pt2D::new(
|
let top_left = Pt2D::new(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::input::ContextMenu;
|
use crate::input::ContextMenu;
|
||||||
use crate::{Canvas, Event, GfxCtx, UserInput};
|
use crate::{Canvas, Event, GfxCtx, UserInput};
|
||||||
use glutin_window::GlutinWindow;
|
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::event_loop::{EventLoop, EventSettings, Events};
|
||||||
use piston::window::{Window, WindowSettings};
|
use piston::window::{Window, WindowSettings};
|
||||||
use std::panic;
|
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 events = Events::new(EventSettings::new().lazy(true));
|
||||||
let mut gl = GlGraphics::new(opengl);
|
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 last_event_mode = EventLoopMode::InputOnly;
|
||||||
let mut context_menu: Option<ContextMenu> = None;
|
let mut context_menu: Option<ContextMenu> = None;
|
||||||
let mut last_data: Option<T> = 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 the very first event is render, then just wait.
|
||||||
if let Some(ref data) = last_data {
|
if let Some(ref data) = last_data {
|
||||||
gl.draw(args.viewport(), |c, g| {
|
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()
|
gui.get_mut_canvas()
|
||||||
.start_drawing(&mut g, window.draw_size());
|
.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.
|
// Always draw the context-menu last.
|
||||||
if let Some(ref mut menu) = context_menu {
|
if let Some(ref mut menu) = context_menu {
|
||||||
// TODO Weird to do this here (all to pass along &mut glyphs), but for the
|
// TODO Can get rid of this weird method now!
|
||||||
// moment, that's how Text::dims works. :\
|
menu.calculate_geometry(gui.get_mut_canvas());
|
||||||
menu.calculate_geometry(&mut g, gui.get_mut_canvas());
|
|
||||||
menu.draw(&mut g, gui.get_mut_canvas());
|
menu.draw(&mut g, gui.get_mut_canvas());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
use crate::{Canvas, Color, GfxCtx};
|
use crate::{Canvas, Color, GfxCtx};
|
||||||
|
use graphics::character::CharacterCache;
|
||||||
use graphics::{Image, Rectangle, Transformed};
|
use graphics::{Image, Rectangle, Transformed};
|
||||||
|
use opengl_graphics::GlyphCache;
|
||||||
use textwrap;
|
use textwrap;
|
||||||
|
|
||||||
pub const TEXT_FG_COLOR: Color = Color([0.0, 0.0, 0.0, 1.0]);
|
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()
|
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
|
let longest_line = self
|
||||||
.lines
|
.lines
|
||||||
.iter()
|
.iter()
|
||||||
@ -108,14 +110,14 @@ impl Text {
|
|||||||
for span in longest_line {
|
for span in longest_line {
|
||||||
concat.push_str(&span.text);
|
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;
|
let height = (self.lines.len() as f64) * LINE_HEIGHT;
|
||||||
(width, height)
|
(width, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_text_bubble(g: &mut GfxCtx, (x1, y1): (f64, f64), txt: Text) {
|
pub fn draw_text_bubble(g: &mut GfxCtx, glyphs: &mut GlyphCache, (x1, y1): (f64, f64), txt: Text) {
|
||||||
let (total_width, total_height) = txt.dims(g);
|
let (total_width, total_height) = txt.dims(glyphs);
|
||||||
Rectangle::new(txt.bg_color.0).draw(
|
Rectangle::new(txt.bg_color.0).draw(
|
||||||
[x1, y1, total_width, total_height],
|
[x1, y1, total_width, total_height],
|
||||||
&g.orig_ctx.draw_state,
|
&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 {
|
for span in line {
|
||||||
if let Some(color) = span.highlight_color {
|
if let Some(color) = span.highlight_color {
|
||||||
// TODO do we ever want to use total_width?
|
// 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(
|
Rectangle::new(color.0).draw(
|
||||||
[x, y - LINE_HEIGHT, width, LINE_HEIGHT],
|
[x, y - LINE_HEIGHT, width, LINE_HEIGHT],
|
||||||
&g.orig_ctx.draw_state,
|
&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);
|
let fg_text = Image::new_color(span.fg_color.0);
|
||||||
|
|
||||||
for ch in span.text.chars() {
|
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
|
let char_ctx = g
|
||||||
.orig_ctx
|
.orig_ctx
|
||||||
.transform
|
.transform
|
||||||
|
Loading…
Reference in New Issue
Block a user