diff --git a/ezgui/src/drawing.rs b/ezgui/src/drawing.rs index cdff24899b..0d8d68284c 100644 --- a/ezgui/src/drawing.rs +++ b/ezgui/src/drawing.rs @@ -630,4 +630,9 @@ impl DrawBoth { pub fn get_dims(&self) -> ScreenDims { self.dims } + + // TODO Hack + pub(crate) fn override_bounds(&mut self, b: Bounds) { + self.dims = ScreenDims::new(b.max_x - b.min_x, b.max_y - b.min_y); + } } diff --git a/ezgui/src/layout.rs b/ezgui/src/layout.rs index 9cf2846186..5b5a48f133 100644 --- a/ezgui/src/layout.rs +++ b/ezgui/src/layout.rs @@ -48,3 +48,13 @@ pub fn stack_vertically( top_left.y += dims.height; } } + +pub fn stack_horizontally(top_left: ScreenPt, padding: f64, widgets: Vec<&mut dyn Widget>) { + let mut x1 = top_left.x; + let y1 = top_left.y; + + for w in widgets { + w.set_pos(ScreenPt::new(x1, y1)); + x1 += w.get_dims().width + padding; + } +} diff --git a/ezgui/src/widgets/no_op.rs b/ezgui/src/widgets/no_op.rs index 4e7bd8a171..e34ccac13c 100644 --- a/ezgui/src/widgets/no_op.rs +++ b/ezgui/src/widgets/no_op.rs @@ -10,6 +10,13 @@ pub struct JustDraw { } impl JustDraw { + pub fn wrap(draw: DrawBoth) -> JustDraw { + JustDraw { + draw, + top_left: ScreenPt::new(0.0, 0.0), + } + } + pub fn image(filename: &str, ctx: &EventCtx) -> JustDraw { let (color, rect) = ctx.canvas.texture_rect(filename); let batch = GeomBatch::from(vec![(color, rect)]); @@ -21,9 +28,12 @@ impl JustDraw { pub fn svg(filename: &str, ctx: &EventCtx) -> JustDraw { let mut batch = GeomBatch::new(); - svg::add_svg(&mut batch, filename); + let bounds = svg::add_svg(&mut batch, filename); + let mut draw = DrawBoth::new(ctx, batch, vec![]); + // TODO The dims will be wrong; it'll only look at geometry, not the padding in the image. + draw.override_bounds(bounds); JustDraw { - draw: DrawBoth::new(ctx, batch, vec![]), + draw, top_left: ScreenPt::new(0.0, 0.0), } } diff --git a/game/assets/meters/bike.svg b/game/assets/meters/bike.svg new file mode 100644 index 0000000000..a70fa1667a --- /dev/null +++ b/game/assets/meters/bike.svg @@ -0,0 +1,3 @@ + + + diff --git a/game/assets/meters/bus.svg b/game/assets/meters/bus.svg new file mode 100644 index 0000000000..fc7016121d --- /dev/null +++ b/game/assets/meters/bus.svg @@ -0,0 +1,3 @@ + + + diff --git a/game/assets/meters/car.svg b/game/assets/meters/car.svg new file mode 100644 index 0000000000..d7206ea7fe --- /dev/null +++ b/game/assets/meters/car.svg @@ -0,0 +1,3 @@ + + + diff --git a/game/assets/meters/pedestrian.svg b/game/assets/meters/pedestrian.svg new file mode 100644 index 0000000000..1e7928508b --- /dev/null +++ b/game/assets/meters/pedestrian.svg @@ -0,0 +1,3 @@ + + + diff --git a/game/src/sandbox/mod.rs b/game/src/sandbox/mod.rs index b96eebb782..ebd1368f41 100644 --- a/game/src/sandbox/mod.rs +++ b/game/src/sandbox/mod.rs @@ -16,17 +16,18 @@ use crate::pregame::main_menu; use crate::ui::{ShowEverything, UI}; use abstutil::Timer; use ezgui::{ - hotkey, layout, lctrl, Choice, EventCtx, EventLoopMode, GfxCtx, Key, Line, MenuUnderButton, - ModalMenu, Text, + hotkey, layout, lctrl, Choice, Color, DrawBoth, EventCtx, EventLoopMode, GeomBatch, GfxCtx, + JustDraw, Key, Line, MenuUnderButton, ModalMenu, ScreenDims, ScreenPt, ScreenRectangle, Text, }; pub use gameplay::spawner::spawn_agents_around; pub use gameplay::GameplayMode; -use geom::Duration; +use geom::{Distance, Duration, Polygon, Time}; use map_model::MapEdits; use sim::TripMode; pub struct SandboxMode { speed: speed::SpeedControls, + agent_meter: AgentMeter, info_tools: MenuUnderButton, general_tools: MenuUnderButton, agent_tools: AgentTools, @@ -41,6 +42,7 @@ impl SandboxMode { pub fn new(ctx: &mut EventCtx, ui: &mut UI, mode: GameplayMode) -> SandboxMode { SandboxMode { speed: speed::SpeedControls::new(ctx, ui.opts.dev), + agent_meter: AgentMeter::new(ctx, ui), general_tools: MenuUnderButton::new( "assets/ui/hamburger.png", "General", @@ -88,16 +90,6 @@ impl State for SandboxMode { fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition { { let mut txt = Text::new(); - let (active, unfinished, by_mode) = ui.primary.sim.num_trips(); - txt.add(Line(format!("Active trips: {}", active))); - txt.add(Line(format!("Unfinished trips: {}", unfinished))); - txt.add(Line(format!( - "Peds {}, Bikes {}, Cars {}, Buses {}", - by_mode[&TripMode::Walk], - by_mode[&TripMode::Bike], - by_mode[&TripMode::Drive], - by_mode[&TripMode::Transit] - ))); txt.add(Line("")); { let edits = ui.primary.map.get_edits(); @@ -108,6 +100,7 @@ impl State for SandboxMode { } self.menu.set_info(ctx, txt); } + self.agent_meter.event(ctx, ui); if let Some(t) = self.gameplay.event(ctx, ui, &mut self.overlay) { return t; } @@ -315,6 +308,7 @@ impl State for SandboxMode { self.info_tools.draw(g); self.general_tools.draw(g); self.gameplay.draw(g, ui); + self.agent_meter.draw(g); if let Some(ref m) = self.minimap { m.draw(g, ui); } @@ -324,3 +318,81 @@ impl State for SandboxMode { self.speed.pause(); } } + +// TODO Some kind of composite thing... +struct AgentMeter { + time: Time, + widgets: Vec, + rect: ScreenRectangle, +} + +impl AgentMeter { + pub fn new(ctx: &EventCtx, ui: &UI) -> AgentMeter { + let (active, unfinished, by_mode) = ui.primary.sim.num_trips(); + + let mut row1_txt = Text::new().no_bg(); + row1_txt.add(Line(format!("Active trips: {}", active))); + row1_txt.add(Line(format!("Unfinished trips: {}", unfinished))); + + // TODO Hardcoding guessed dims + let rect_bg = GeomBatch::from(vec![( + Color::grey(0.4), + Polygon::rounded_rectangle( + Distance::meters(290.0), + Distance::meters(100.0), + Distance::meters(5.0), + ), + )]); + + // TODO Rectangle behind everything + let mut widgets = vec![ + JustDraw::wrap(DrawBoth::new(ctx, rect_bg, Vec::new())), + JustDraw::text(row1_txt, ctx), + JustDraw::svg("assets/meters/pedestrian.svg", ctx), + JustDraw::text(Text::from(Line(&by_mode[&TripMode::Walk])).no_bg(), ctx), + JustDraw::svg("assets/meters/bike.svg", ctx), + JustDraw::text(Text::from(Line(&by_mode[&TripMode::Bike])).no_bg(), ctx), + JustDraw::svg("assets/meters/car.svg", ctx), + JustDraw::text(Text::from(Line(&by_mode[&TripMode::Drive])).no_bg(), ctx), + JustDraw::svg("assets/meters/bus.svg", ctx), + JustDraw::text(Text::from(Line(&by_mode[&TripMode::Transit])).no_bg(), ctx), + ]; + + // TODO A horrible experiment in manual layouting + use layout::Widget; + + let top_left = ScreenPt::new(ctx.canvas.window_width - 300.0, 350.0); + widgets[0].set_pos(top_left); + widgets[1].set_pos(top_left); + let top_left = ScreenPt::new(top_left.x, top_left.y + widgets[1].get_dims().height); + layout::stack_horizontally( + top_left, + // TODO Padding is wrong, want to alternate the amount + 5.0, + widgets + .iter_mut() + .skip(2) + .map(|w| w as &mut dyn Widget) + .collect(), + ); + AgentMeter { + widgets, + time: ui.primary.sim.time(), + rect: ScreenRectangle::top_left(top_left, ScreenDims::new(290.0, 100.0)), + } + } + + pub fn event(&mut self, ctx: &EventCtx, ui: &UI) { + // TODO Or window size changed... + if self.time != ui.primary.sim.time() { + *self = AgentMeter::new(ctx, ui); + } + } + + pub fn draw(&self, g: &mut GfxCtx) { + for w in &self.widgets { + w.draw(g); + } + g.canvas.mark_covered_area(self.rect.clone()); + } +}