mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-29 04:35:51 +03:00
calculating line height from the font
This commit is contained in:
parent
9c94e2f374
commit
f1b81456a6
@ -66,9 +66,9 @@
|
|||||||
## Switch to OpenGL (for speed)
|
## Switch to OpenGL (for speed)
|
||||||
|
|
||||||
- no bugs
|
- no bugs
|
||||||
- text entry needs to draw the cursor differently anyway.
|
- text entry needs to draw the cursor differently
|
||||||
- top menu is very buggy
|
- top menu is very buggy. MAX_CHAR_WIDTH is weird
|
||||||
- need padding around text; and selecting stuff from menus is weird. LINE_HEIGHT.
|
- need padding around text; and selecting stuff from menus is weird
|
||||||
- forking is buggy (traffic signal diagram)
|
- forking is buggy (traffic signal diagram)
|
||||||
- arrows
|
- arrows
|
||||||
- some colors are wrong
|
- some colors are wrong
|
||||||
@ -76,4 +76,7 @@
|
|||||||
- make polygon store points and indices efficiently
|
- make polygon store points and indices efficiently
|
||||||
- change ezgui API to allow uploading geometry once
|
- change ezgui API to allow uploading geometry once
|
||||||
- undo the y inversion hacks at last!
|
- undo the y inversion hacks at last!
|
||||||
- probably use f32, not f64 everywhere
|
- refactoring
|
||||||
|
- pass canvas to text module, make it do the glyph borrowing?
|
||||||
|
- pass dims to draw_text_bubble; all callers have it anyway, right?
|
||||||
|
- probably use f32, not f64 everywhere
|
||||||
|
@ -2,7 +2,7 @@ use crate::objects::{Ctx, ID};
|
|||||||
use crate::plugins::{Plugin, PluginCtx};
|
use crate::plugins::{Plugin, PluginCtx};
|
||||||
use crate::render::{draw_signal_diagram, DrawTurn};
|
use crate::render::{draw_signal_diagram, DrawTurn};
|
||||||
use dimensioned::si;
|
use dimensioned::si;
|
||||||
use ezgui::{Color, GfxCtx, Key, TOP_MENU_HEIGHT};
|
use ezgui::{Color, GfxCtx, Key};
|
||||||
use map_model::{IntersectionID, LaneID, TurnType};
|
use map_model::{IntersectionID, LaneID, TurnType};
|
||||||
|
|
||||||
pub struct TurnCyclerState {
|
pub struct TurnCyclerState {
|
||||||
@ -101,7 +101,7 @@ impl Plugin for TurnCyclerState {
|
|||||||
i,
|
i,
|
||||||
cycle.idx,
|
cycle.idx,
|
||||||
Some(time_left),
|
Some(time_left),
|
||||||
TOP_MENU_HEIGHT + 10.0,
|
ctx.canvas.top_menu_height() + 10.0,
|
||||||
g,
|
g,
|
||||||
ctx,
|
ctx,
|
||||||
);
|
);
|
||||||
|
@ -25,6 +25,7 @@ pub struct Canvas {
|
|||||||
|
|
||||||
// TODO Super gross, but we can't create this immediately.
|
// TODO Super gross, but we can't create this immediately.
|
||||||
pub(crate) glyphs: RefCell<Option<GlyphBrush<'static, 'static>>>,
|
pub(crate) glyphs: RefCell<Option<GlyphBrush<'static, 'static>>>,
|
||||||
|
pub(crate) line_height: f64,
|
||||||
|
|
||||||
// TODO Bit weird and hacky to mutate inside of draw() calls.
|
// TODO Bit weird and hacky to mutate inside of draw() calls.
|
||||||
covered_areas: RefCell<Vec<ScreenRectangle>>,
|
covered_areas: RefCell<Vec<ScreenRectangle>>,
|
||||||
@ -46,6 +47,7 @@ impl Canvas {
|
|||||||
window_height: f64::from(initial_height),
|
window_height: f64::from(initial_height),
|
||||||
|
|
||||||
glyphs: RefCell::new(None),
|
glyphs: RefCell::new(None),
|
||||||
|
line_height: 0.0,
|
||||||
covered_areas: RefCell::new(Vec::new()),
|
covered_areas: RefCell::new(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +97,7 @@ 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 mut glyphs = self.glyphs.borrow_mut();
|
let mut glyphs = self.glyphs.borrow_mut();
|
||||||
let (width, height) = txt.dims(glyphs.as_mut().unwrap());
|
let (width, height) = txt.dims(glyphs.as_mut().unwrap(), self);
|
||||||
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);
|
||||||
// No need to cover the tooltip; this tooltip follows the mouse anyway.
|
// No need to cover the tooltip; this tooltip follows the mouse anyway.
|
||||||
@ -111,7 +113,7 @@ impl Canvas {
|
|||||||
// TODO Rename these draw_nonblocking_text_*
|
// TODO Rename these draw_nonblocking_text_*
|
||||||
pub fn draw_text_at(&self, g: &mut GfxCtx, txt: Text, map_pt: Pt2D) {
|
pub fn draw_text_at(&self, g: &mut GfxCtx, txt: Text, map_pt: Pt2D) {
|
||||||
let mut glyphs = self.glyphs.borrow_mut();
|
let mut glyphs = self.glyphs.borrow_mut();
|
||||||
let (width, height) = txt.dims(glyphs.as_mut().unwrap());
|
let (width, height) = txt.dims(glyphs.as_mut().unwrap(), self);
|
||||||
let pt = self.map_to_screen(map_pt);
|
let pt = self.map_to_screen(map_pt);
|
||||||
text::draw_text_bubble(
|
text::draw_text_bubble(
|
||||||
g,
|
g,
|
||||||
@ -147,7 +149,7 @@ impl Canvas {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut glyphs = self.glyphs.borrow_mut();
|
let mut glyphs = self.glyphs.borrow_mut();
|
||||||
let (width, height) = txt.dims(glyphs.as_mut().unwrap());
|
let (width, height) = txt.dims(glyphs.as_mut().unwrap(), self);
|
||||||
let x1 = match horiz {
|
let x1 = match horiz {
|
||||||
HorizontalAlignment::Left => 0.0,
|
HorizontalAlignment::Left => 0.0,
|
||||||
HorizontalAlignment::Center => (self.window_width - width) / 2.0,
|
HorizontalAlignment::Center => (self.window_width - width) / 2.0,
|
||||||
@ -155,7 +157,7 @@ impl Canvas {
|
|||||||
};
|
};
|
||||||
let y1 = match vert {
|
let y1 = match vert {
|
||||||
VerticalAlignment::Top => 0.0,
|
VerticalAlignment::Top => 0.0,
|
||||||
VerticalAlignment::BelowTopMenu => text::LINE_HEIGHT,
|
VerticalAlignment::BelowTopMenu => self.line_height,
|
||||||
VerticalAlignment::Center => (self.window_height - height) / 2.0,
|
VerticalAlignment::Center => (self.window_height - height) / 2.0,
|
||||||
VerticalAlignment::Bottom => self.window_height - height,
|
VerticalAlignment::Bottom => self.window_height - height,
|
||||||
};
|
};
|
||||||
@ -169,7 +171,7 @@ impl Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_dims(&self, txt: &Text) -> (f64, f64) {
|
pub fn text_dims(&self, txt: &Text) -> (f64, f64) {
|
||||||
txt.dims(self.glyphs.borrow_mut().as_mut().unwrap())
|
txt.dims(self.glyphs.borrow_mut().as_mut().unwrap(), self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zoom_towards_mouse(&mut self, delta_zoom: f64) {
|
fn zoom_towards_mouse(&mut self, delta_zoom: f64) {
|
||||||
@ -237,6 +239,12 @@ impl Canvas {
|
|||||||
b.update(self.screen_to_map(ScreenPt::new(self.window_width, self.window_height)));
|
b.update(self.screen_to_map(ScreenPt::new(self.window_width, self.window_height)));
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Not super happy about exposing this; fork_screenspace for external callers should be
|
||||||
|
// smarter.
|
||||||
|
pub fn top_menu_height(&self) -> f64 {
|
||||||
|
self.line_height
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum HorizontalAlignment {
|
pub enum HorizontalAlignment {
|
||||||
|
@ -27,10 +27,6 @@ pub use crate::wizard::{Wizard, WrappedWizard};
|
|||||||
use geom::{Angle, Circle, Line, Polygon, Pt2D, Triangle};
|
use geom::{Angle, Circle, Line, Polygon, Pt2D, Triangle};
|
||||||
use glium::{implement_vertex, uniform, Surface};
|
use glium::{implement_vertex, uniform, Surface};
|
||||||
|
|
||||||
// TODO Not super happy about exposing this; fork_screenspace for external callers should be
|
|
||||||
// smarter.
|
|
||||||
pub const TOP_MENU_HEIGHT: f64 = text::LINE_HEIGHT;
|
|
||||||
|
|
||||||
pub struct ToggleableLayer {
|
pub struct ToggleableLayer {
|
||||||
layer_name: String,
|
layer_name: String,
|
||||||
// If None, never automatically enable at a certain zoom level.
|
// If None, never automatically enable at a certain zoom level.
|
||||||
|
@ -66,7 +66,7 @@ impl LogScroller {
|
|||||||
let can_fit = {
|
let can_fit = {
|
||||||
// Subtract 1 for the title, and an additional TODO hacky
|
// Subtract 1 for the title, and an additional TODO hacky
|
||||||
// few to avoid the bottom OSD and stuff.
|
// few to avoid the bottom OSD and stuff.
|
||||||
let n = (canvas.window_height / text::LINE_HEIGHT).floor() as isize - 1 - 6;
|
let n = (canvas.window_height / canvas.line_height).floor() as isize - 1 - 6;
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::screen_geom::ScreenRectangle;
|
use crate::screen_geom::ScreenRectangle;
|
||||||
use crate::text::LINE_HEIGHT;
|
|
||||||
use crate::{text, Canvas, Event, GfxCtx, InputResult, Key, ScreenPt, Text};
|
use crate::{text, Canvas, Event, GfxCtx, InputResult, Key, ScreenPt, Text};
|
||||||
|
|
||||||
// Stores some associated data with each choice
|
// Stores some associated data with each choice
|
||||||
@ -66,7 +65,7 @@ impl<T: Clone> Menu<T> {
|
|||||||
} else {
|
} else {
|
||||||
total_width
|
total_width
|
||||||
};
|
};
|
||||||
ScreenPt::new(canvas.window_width - w, LINE_HEIGHT)
|
ScreenPt::new(canvas.window_width - w, canvas.line_height)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use crate::input::{ContextMenu, ModalMenuState};
|
use crate::input::{ContextMenu, ModalMenuState};
|
||||||
use crate::{Canvas, Event, GfxCtx, ModalMenu, TopMenu, UserInput};
|
use crate::{text, Canvas, Event, GfxCtx, ModalMenu, TopMenu, UserInput};
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use glium::glutin;
|
use glium::glutin;
|
||||||
use glium_glyph::glyph_brush::rusttype::Font;
|
use glium_glyph::glyph_brush::rusttype::Font;
|
||||||
|
use glium_glyph::glyph_brush::rusttype::Scale;
|
||||||
use glium_glyph::GlyphBrush;
|
use glium_glyph::GlyphBrush;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@ -275,6 +276,10 @@ pub fn run<T, G: GUI<T>>(mut gui: G, window_title: &str) {
|
|||||||
|
|
||||||
let dejavu: &[u8] = include_bytes!("DejaVuSans.ttf");
|
let dejavu: &[u8] = include_bytes!("DejaVuSans.ttf");
|
||||||
let fonts = vec![Font::from_bytes(dejavu).unwrap()];
|
let fonts = vec![Font::from_bytes(dejavu).unwrap()];
|
||||||
|
let vmetrics = fonts[0].v_metrics(Scale::uniform(text::FONT_SIZE));
|
||||||
|
// TODO This works for this font, but could be more paranoid with abs()
|
||||||
|
gui.get_mut_canvas().line_height =
|
||||||
|
(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap) as f64;
|
||||||
gui.get_mut_canvas().glyphs = RefCell::new(Some(GlyphBrush::new(&display, fonts)));
|
gui.get_mut_canvas().glyphs = RefCell::new(Some(GlyphBrush::new(&display, fonts)));
|
||||||
|
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
|
@ -59,7 +59,7 @@ impl<T: Clone> ScrollingMenu<T> {
|
|||||||
let can_fit = {
|
let can_fit = {
|
||||||
// Subtract 1 for the prompt, and an additional TODO hacky
|
// Subtract 1 for the prompt, and an additional TODO hacky
|
||||||
// few to avoid the bottom OSD and stuff.
|
// few to avoid the bottom OSD and stuff.
|
||||||
let n = (canvas.window_height / text::LINE_HEIGHT).floor() as isize - 1 - 6;
|
let n = (canvas.window_height / canvas.line_height).floor() as isize - 1 - 6;
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
// Weird small window, just display the prompt and bail out.
|
// Weird small window, just display the prompt and bail out.
|
||||||
canvas.draw_blocking_text(g, txt, CENTERED);
|
canvas.draw_blocking_text(g, txt, CENTERED);
|
||||||
|
@ -14,10 +14,8 @@ pub const SELECTED_COLOR: Color = Color::RED;
|
|||||||
pub const HOTKEY_COLOR: Color = Color::GREEN;
|
pub const HOTKEY_COLOR: Color = Color::GREEN;
|
||||||
pub const INACTIVE_CHOICE_COLOR: Color = Color::grey(0.4);
|
pub const INACTIVE_CHOICE_COLOR: Color = Color::grey(0.4);
|
||||||
|
|
||||||
const FONT_SIZE: f32 = 24.0;
|
pub const FONT_SIZE: f32 = 24.0;
|
||||||
// TODO These are dependent on FONT_SIZE, but hand-tuned. Glyphs all have 0 as their height, and
|
// TODO Don't do this!
|
||||||
// they need adjustments to their positioning.
|
|
||||||
pub const LINE_HEIGHT: f64 = 32.0;
|
|
||||||
const MAX_CHAR_WIDTH: f64 = 25.0;
|
const MAX_CHAR_WIDTH: f64 = 25.0;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -117,34 +115,36 @@ impl Text {
|
|||||||
self.lines.is_empty()
|
self.lines.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn dims(&self, glyphs: &mut GlyphBrush<'static, 'static>) -> (f64, f64) {
|
pub(crate) fn dims(
|
||||||
let mut widths: Vec<i32> = Vec::new();
|
&self,
|
||||||
let mut total_height: i32 = 0;
|
glyphs: &mut GlyphBrush<'static, 'static>,
|
||||||
|
canvas: &Canvas,
|
||||||
|
) -> (f64, f64) {
|
||||||
|
let width = self
|
||||||
|
.lines
|
||||||
|
.iter()
|
||||||
|
.map(|(_, l)| {
|
||||||
|
let full_line = l.iter().fold(String::new(), |mut so_far, span| {
|
||||||
|
so_far.push_str(&span.text);
|
||||||
|
so_far
|
||||||
|
});
|
||||||
|
if let Some(rect) = glyphs.pixel_bounds(Section {
|
||||||
|
text: &full_line,
|
||||||
|
scale: Scale::uniform(FONT_SIZE),
|
||||||
|
..Section::default()
|
||||||
|
}) {
|
||||||
|
rect.width()
|
||||||
|
} else {
|
||||||
|
// TODO Sometimes we want to space something like " ", but no drawn glyphs
|
||||||
|
// means pixel_bounds fails. Hack?
|
||||||
|
(MAX_CHAR_WIDTH * (full_line.len() as f64)) as i32
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap() as f64;
|
||||||
|
|
||||||
for (_, l) in &self.lines {
|
// Always use the max height, since other stuff like menus assume a fixed height.
|
||||||
let full_line = l.iter().fold(String::new(), |mut so_far, span| {
|
(width, (self.lines.len() as f64) * canvas.line_height)
|
||||||
so_far.push_str(&span.text);
|
|
||||||
so_far
|
|
||||||
});
|
|
||||||
if let Some(rect) = glyphs.pixel_bounds(Section {
|
|
||||||
text: &full_line,
|
|
||||||
scale: Scale::uniform(FONT_SIZE),
|
|
||||||
..Section::default()
|
|
||||||
}) {
|
|
||||||
widths.push(rect.width());
|
|
||||||
total_height += rect.height();
|
|
||||||
} else {
|
|
||||||
// TODO Sometimes we want to space something like " ", but no drawn glyphs
|
|
||||||
// means pixel_bounds fails. Hack?
|
|
||||||
widths.push((MAX_CHAR_WIDTH * (full_line.len() as f64)) as i32);
|
|
||||||
total_height += LINE_HEIGHT as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
widths.into_iter().max().unwrap() as f64,
|
|
||||||
total_height as f64,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ pub fn draw_text_bubble(
|
|||||||
// TODO Is it expensive to constantly change uniforms and the shader program?
|
// TODO Is it expensive to constantly change uniforms and the shader program?
|
||||||
g.fork_screenspace(canvas);
|
g.fork_screenspace(canvas);
|
||||||
|
|
||||||
let (total_width, total_height) = txt.dims(glyphs);
|
let (total_width, total_height) = txt.dims(glyphs, canvas);
|
||||||
if let Some(c) = txt.bg_color {
|
if let Some(c) = txt.bg_color {
|
||||||
g.draw_polygon(
|
g.draw_polygon(
|
||||||
c,
|
c,
|
||||||
@ -185,16 +185,19 @@ pub fn draw_text_bubble(
|
|||||||
.collect(),
|
.collect(),
|
||||||
..VariedSection::default()
|
..VariedSection::default()
|
||||||
};
|
};
|
||||||
let height = glyphs.pixel_bounds(section.clone()).unwrap().height() as f64;
|
|
||||||
|
|
||||||
if let Some(c) = line_color {
|
if let Some(c) = line_color {
|
||||||
g.draw_polygon(
|
g.draw_polygon(
|
||||||
*c,
|
*c,
|
||||||
&Polygon::rectangle_topleft(Pt2D::new(top_left.x, y), total_width, height),
|
&Polygon::rectangle_topleft(
|
||||||
|
Pt2D::new(top_left.x, y),
|
||||||
|
total_width,
|
||||||
|
canvas.line_height,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
y += height;
|
y += canvas.line_height;
|
||||||
glyphs.queue(section);
|
glyphs.queue(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::menu::{Menu, Position};
|
use crate::menu::{Menu, Position};
|
||||||
use crate::screen_geom::ScreenRectangle;
|
use crate::screen_geom::ScreenRectangle;
|
||||||
use crate::text;
|
use crate::text;
|
||||||
use crate::TOP_MENU_HEIGHT;
|
|
||||||
use crate::{Canvas, GfxCtx, InputResult, Key, ScreenPt, Text, UserInput};
|
use crate::{Canvas, GfxCtx, InputResult, Key, ScreenPt, Text, UserInput};
|
||||||
use geom::{Polygon, Pt2D};
|
use geom::{Polygon, Pt2D};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@ -132,13 +131,17 @@ impl TopMenu {
|
|||||||
x1: 0.0,
|
x1: 0.0,
|
||||||
y1: 0.0,
|
y1: 0.0,
|
||||||
x2: canvas.window_width,
|
x2: canvas.window_width,
|
||||||
y2: TOP_MENU_HEIGHT,
|
y2: canvas.line_height,
|
||||||
});
|
});
|
||||||
|
|
||||||
g.fork_screenspace(canvas);
|
g.fork_screenspace(canvas);
|
||||||
g.draw_polygon(
|
g.draw_polygon(
|
||||||
text::BG_COLOR,
|
text::BG_COLOR,
|
||||||
&Polygon::rectangle_topleft(Pt2D::new(0.0, 0.0), canvas.window_width, TOP_MENU_HEIGHT),
|
&Polygon::rectangle_topleft(
|
||||||
|
Pt2D::new(0.0, 0.0),
|
||||||
|
canvas.window_width,
|
||||||
|
canvas.line_height,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(idx) = self.highlighted {
|
if let Some(idx) = self.highlighted {
|
||||||
|
Loading…
Reference in New Issue
Block a user