diff --git a/ezgui/src/assets.rs b/ezgui/src/assets.rs index 30648eaccd..09f8b96623 100644 --- a/ezgui/src/assets.rs +++ b/ezgui/src/assets.rs @@ -29,7 +29,7 @@ impl Assets { text_opts: Options::default(), }; *a.default_line_height.borrow_mut() = - a.line_height(Font::OverpassRegular, *a.default_font_size.borrow()); + a.line_height(text::DEFAULT_FONT, *a.default_font_size.borrow()); a.text_opts.font_directories.push(font_dir); a } @@ -92,6 +92,6 @@ impl Assets { self.line_height_cache.borrow_mut().clear(); self.svg_cache.borrow_mut().clear(); *self.default_line_height.borrow_mut() = - self.line_height(Font::OverpassRegular, *self.default_font_size.borrow()); + self.line_height(text::DEFAULT_FONT, *self.default_font_size.borrow()); } } diff --git a/ezgui/src/runner.rs b/ezgui/src/runner.rs index 311b308d95..72a2ec642f 100644 --- a/ezgui/src/runner.rs +++ b/ezgui/src/runner.rs @@ -1,6 +1,6 @@ use crate::assets::Assets; use crate::tools::screenshot::{screenshot_current, screenshot_everything}; -use crate::{Canvas, Event, EventCtx, GfxCtx, Key, Prerender, UserInput}; +use crate::{text, Canvas, Event, EventCtx, GfxCtx, Key, Prerender, UserInput}; use geom::Duration; use instant::Instant; use std::cell::Cell; @@ -169,7 +169,7 @@ impl Settings { window_title: window_title.to_string(), font_dir: font_dir.to_string(), profiling_enabled: false, - default_font_size: 21, + default_font_size: text::DEFAULT_FONT_SIZE, dump_raw_events: false, scale_factor: 1.0, } diff --git a/ezgui/src/text.rs b/ezgui/src/text.rs index 97c4e2525e..3dab6a6013 100644 --- a/ezgui/src/text.rs +++ b/ezgui/src/text.rs @@ -8,7 +8,11 @@ use std::fmt::Write; use std::hash::Hasher; use textwrap; -const FG_COLOR: Color = Color::WHITE; +// Same as body() +pub const DEFAULT_FONT: Font = Font::OverpassRegular; +pub const DEFAULT_FONT_SIZE: usize = 21; +const DEFAULT_FG_COLOR: Color = Color::WHITE; + pub const BG_COLOR: Color = Color::grey(0.3); pub const SELECTED_COLOR: Color = Color::grey(0.5); pub const HOTKEY_COLOR: Color = Color::GREEN; @@ -31,13 +35,13 @@ pub enum Font { pub struct TextSpan { text: String, fg_color: Color, - size: Option, + size: usize, font: Font, } impl TextSpan { pub fn fg(mut self, color: Color) -> TextSpan { - assert_eq!(self.fg_color, FG_COLOR); + assert_eq!(self.fg_color, DEFAULT_FG_COLOR); self.fg_color = color; self } @@ -50,39 +54,39 @@ impl TextSpan { pub fn display_title(mut self) -> TextSpan { self.font = Font::BungeeInlineRegular; - self.size = Some(64); + self.size = 64; self } pub fn big_heading_styled(mut self) -> TextSpan { self.font = Font::BungeeRegular; - self.size = Some(32); + self.size = 32; self } pub fn big_heading_plain(mut self) -> TextSpan { self.font = Font::OverpassBold; - self.size = Some(32); + self.size = 32; self } pub fn small_heading(mut self) -> TextSpan { self.font = Font::OverpassSemiBold; - self.size = Some(26); + self.size = 26; self } // The default pub fn body(mut self) -> TextSpan { self.font = Font::OverpassRegular; - self.size = Some(21); + self.size = 21; self } pub fn secondary(mut self) -> TextSpan { self.font = Font::OverpassRegular; - self.size = Some(21); + self.size = 21; self.fg_color = Color::hex("#A3A3A3"); self } pub fn small(mut self) -> TextSpan { self.font = Font::OverpassRegular; - self.size = Some(16); + self.size = 16; self } } @@ -92,9 +96,9 @@ impl TextSpan { pub fn Line>(text: S) -> TextSpan { TextSpan { text: text.into(), - fg_color: FG_COLOR, - size: None, - font: Font::OverpassRegular, + fg_color: DEFAULT_FG_COLOR, + size: DEFAULT_FONT_SIZE, + font: DEFAULT_FONT, } } @@ -120,6 +124,14 @@ impl Text { txt } + pub fn from_all(lines: Vec) -> Text { + let mut txt = Text::new(); + for l in lines { + txt.append(l); + } + txt + } + // TODO Remove this pub fn with_bg(mut self) -> Text { assert!(self.bg_color.is_none()); @@ -136,9 +148,10 @@ impl Text { // TODO Not exactly sure this is the right place for this, but better than code duplication pub fn tooltip(hotkey: Option, action: &str) -> Text { if let Some(ref key) = hotkey { - let mut txt = Text::from(Line(key.describe()).fg(HOTKEY_COLOR).small()); - txt.append(Line(format!(" - {}", action))); - txt + Text::from_all(vec![ + Line(key.describe()).fg(HOTKEY_COLOR).small(), + Line(format!(" - {}", action)).small(), + ]) } else { Text::from(Line(action).small()) } @@ -166,22 +179,16 @@ impl Text { self.lines.last_mut().unwrap().0 = Some(highlight); } - pub fn append(&mut self, mut line: TextSpan) { + pub fn append(&mut self, line: TextSpan) { if self.lines.is_empty() { self.add(line); return; } - // Can't override the size mid-line. - assert_eq!(line.size, None); - line.size = self - .lines - .last() - .unwrap() - .1 - .last() - .map(|span| span.size) - .unwrap(); + // Can't override the size or font mid-line. + let last = self.lines.last().unwrap().1.last().unwrap(); + assert_eq!(line.size, last.size); + assert_eq!(line.font, last.font); self.lines.last_mut().unwrap().1.push(line); } @@ -203,6 +210,7 @@ impl Text { } } + // TODO Not at all correct! pub fn add_wrapped(&mut self, line: String, width: f64) { let wrap_to = width / MAX_CHAR_WIDTH; for l in textwrap::wrap(&line, wrap_to as usize).into_iter() { @@ -247,10 +255,7 @@ impl Text { for (line_color, line) in self.lines { // Assume size doesn't change mid-line. Always use this fixed line height per font // size. - let line_height = assets.line_height( - line[0].font, - line[0].size.unwrap_or(*assets.default_font_size.borrow()), - ); + let line_height = assets.line_height(line[0].font, line[0].size); let line_batch = render_text(line, tolerance, assets); let line_dims = if line_batch.is_empty() { @@ -311,11 +316,15 @@ impl Text { fn render_text(spans: Vec, tolerance: f32, assets: &Assets) -> GeomBatch { // TODO This assumes size and font don't change mid-line. We might be able to support that now, // actually. + // https://www.oreilly.com/library/view/svg-text-layout/9781491933817/ch04.html // Just set a sufficiently large view box - let mut svg = format!( - r##""##, - spans[0].size.unwrap_or(*assets.default_font_size.borrow()), + let mut svg = r##""##.to_string(); + + write!( + &mut svg, + r##""##, + spans[0].size, match spans[0].font { Font::BungeeInlineRegular => "font-family=\"Bungee Inline\"", Font::BungeeRegular => "font-family=\"Bungee\"", @@ -323,7 +332,8 @@ fn render_text(spans: Vec, tolerance: f32, assets: &Assets) -> GeomBat Font::OverpassRegular => "font-family=\"Overpass\"", Font::OverpassSemiBold => "font-family=\"Overpass\" font-weight=\"600\"", } - ); + ) + .unwrap(); let mut contents = String::new(); for span in spans { @@ -338,8 +348,6 @@ fn render_text(spans: Vec, tolerance: f32, assets: &Assets) -> GeomBat } write!(&mut svg, "{}", contents).unwrap(); - //println!("- Rendering: {}", contents); - let svg_tree = match usvg::Tree::from_str(&svg, &assets.text_opts) { Ok(t) => t, Err(err) => panic!("render_text({}): {}", contents, err), diff --git a/game/src/info/person.rs b/game/src/info/person.rs index b9dccbb09d..b88b4dd7fe 100644 --- a/game/src/info/person.rs +++ b/game/src/info/person.rs @@ -2,7 +2,7 @@ use crate::app::App; use crate::colors; use crate::info::{building, header_btns, make_table, make_tabs, trip, Details, Tab}; use crate::render::Renderable; -use ezgui::{hotkey, Btn, Color, EventCtx, Key, Line, RewriteColor, TextExt, Widget}; +use ezgui::{hotkey, Btn, Color, EventCtx, Key, Line, RewriteColor, Text, TextExt, Widget}; use map_model::Map; use maplit::btreeset; use sim::{ @@ -60,8 +60,11 @@ pub fn trips( // TODO Style wrong. Button should be the entire row. rows.push( Widget::row(vec![ - t.to_string().draw_text(ctx), - Line(trip_mode.ongoing_verb()).secondary().draw(ctx), + Text::from_all(vec![ + Line(format!("{} ", t)), + Line(trip_mode.ongoing_verb()).secondary(), + ]) + .draw(ctx), if trip_status == "ongoing" { // TODO Padding doesn't work without wrapping in a row Widget::row(vec![Line(trip_status) diff --git a/game/src/info/trip.rs b/game/src/info/trip.rs index 61ad84d1ea..fae1479814 100644 --- a/game/src/info/trip.rs +++ b/game/src/info/trip.rs @@ -56,30 +56,28 @@ pub fn details(ctx: &mut EventCtx, app: &App, trip: TripID, details: &mut Detail }, }; - // TODO Can we change font mid line please? col.push(Widget::row(vec![ Widget::row(vec![Line("Trip time").secondary().draw(ctx)]).force_width(ctx, col_width), - props.total_time.to_string().draw_text(ctx), - Line(format!("{} / {} this trip", activity, total_trip_time)) - .secondary() - .draw(ctx), + Text::from_all(vec![ + Line(props.total_time.to_string()), + Line(format!(" {} / {} this trip", activity, total_trip_time)).secondary(), + ]) + .draw(ctx), ])); col.push(Widget::row(vec![ Widget::row(vec![Line("Distance").secondary().draw(ctx)]).force_width(ctx, col_width), Widget::col(vec![ - Widget::row(vec![ - props.dist_crossed.describe_rounded().draw_text(ctx), - Line(format!("/{}", props.total_dist.describe_rounded())) - .secondary() - .draw(ctx), - ]), - Widget::row(vec![ - format!("{} lanes", props.lanes_crossed).draw_text(ctx), - Line(format!("/{}", props.total_lanes)) - .secondary() - .draw(ctx), - ]), + Text::from_all(vec![ + Line(props.dist_crossed.describe_rounded()), + Line(format!("/{}", props.total_dist.describe_rounded())).secondary(), + ]) + .draw(ctx), + Text::from_all(vec![ + Line(format!("{} lanes", props.lanes_crossed)), + Line(format!("/{}", props.total_lanes)).secondary(), + ]) + .draw(ctx), ]), ])); @@ -87,18 +85,18 @@ pub fn details(ctx: &mut EventCtx, app: &App, trip: TripID, details: &mut Detail Widget::row(vec![Line("Waiting").secondary().draw(ctx)]).force_width(ctx, col_width), Widget::col(vec![ format!("{} here", props.waiting_here).draw_text(ctx), - Widget::row(vec![ - (if props.total_waiting != Duration::ZERO { - format!( + Text::from_all(vec![ + if props.total_waiting != Duration::ZERO { + Line(format!( "{}%", (100.0 * (props.waiting_here / props.total_waiting)) as usize - ) + )) } else { - "0%".to_string() - }) - .draw_text(ctx), - Line(format!(" of {} time", activity)).secondary().draw(ctx), - ]), + Line("0%") + }, + Line(format!(" of {} time", activity)).secondary(), + ]) + .draw(ctx), ]), ]));