From eda40ecdec2d8ae1dec2f130a266039ba437e725 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Mon, 16 Dec 2019 15:40:32 -0800 Subject: [PATCH] start an interactive legend for the minimap. pretty disjoint from real AgentColorScheme right now --- ezgui/src/color.rs | 2 + game/assets/tools/visibility.svg | 3 + game/src/abtest/mod.rs | 1 - game/src/common/agent.rs | 29 +----- game/src/common/colors.rs | 1 - game/src/common/minimap.rs | 146 +++++++++++++++++++++++++++++-- game/src/managed.rs | 5 ++ game/src/sandbox/mod.rs | 7 +- 8 files changed, 156 insertions(+), 38 deletions(-) create mode 100644 game/assets/tools/visibility.svg diff --git a/ezgui/src/color.rs b/ezgui/src/color.rs index f38b2a7830..47c17190dd 100644 --- a/ezgui/src/color.rs +++ b/ezgui/src/color.rs @@ -45,6 +45,8 @@ impl fmt::Display for Color { } impl Color { + // TODO Won't this confuse the shader? :P + pub const INVISIBLE: Color = Color::rgba_f(1.0, 0.0, 0.0, 0.0); pub const BLACK: Color = Color::rgb_f(0.0, 0.0, 0.0); pub const WHITE: Color = Color::rgb_f(1.0, 1.0, 1.0); pub const RED: Color = Color::rgb_f(1.0, 0.0, 0.0); diff --git a/game/assets/tools/visibility.svg b/game/assets/tools/visibility.svg new file mode 100644 index 0000000000..fc8990785d --- /dev/null +++ b/game/assets/tools/visibility.svg @@ -0,0 +1,3 @@ + + + diff --git a/game/src/abtest/mod.rs b/game/src/abtest/mod.rs index 3839c34e58..1f91a4766f 100644 --- a/game/src/abtest/mod.rs +++ b/game/src/abtest/mod.rs @@ -38,7 +38,6 @@ impl ABTestMode { (hotkey(Key::D), "diff all trips"), (hotkey(Key::A), "stop diffing trips"), (hotkey(Key::O), "save state"), - (hotkey(Key::Semicolon), "change agent colorscheme"), // TODO load arbitrary savestate ], ctx, diff --git a/game/src/common/agent.rs b/game/src/common/agent.rs index 7f239c2bd3..681ee5eced 100644 --- a/game/src/common/agent.rs +++ b/game/src/common/agent.rs @@ -1,9 +1,9 @@ use crate::common::route_viewer::RouteViewer; use crate::common::{RouteExplorer, TripExplorer}; -use crate::game::{msg, Transition, WizardState}; -use crate::render::{AgentColorScheme, MIN_ZOOM_FOR_DETAIL}; +use crate::game::{msg, Transition}; +use crate::render::MIN_ZOOM_FOR_DETAIL; use crate::ui::UI; -use ezgui::{hotkey, Choice, EventCtx, GfxCtx, Key, ModalMenu}; +use ezgui::{hotkey, EventCtx, GfxCtx, Key, ModalMenu}; use geom::{Pt2D, Time}; use sim::{TripID, TripResult}; @@ -83,29 +83,6 @@ impl AgentTools { } self.route_viewer.event(ctx, ui, menu); - if menu.action("change agent colorscheme") { - return Some(Transition::Push(WizardState::new(Box::new( - |wiz, ctx, ui| { - let (_, acs) = wiz.wrap(ctx).choose("Which colorscheme for agents?", || { - let mut choices = Vec::new(); - for (acs, name) in AgentColorScheme::all() { - if ui.agent_cs != acs { - choices.push(Choice::new(name, acs)); - } - } - choices - })?; - ui.agent_cs = acs; - ui.agent_cs_legend = acs.make_color_legend(ctx, &ui.cs); - ui.primary.draw_map.agents.borrow_mut().invalidate_cache(); - if let Some(ref mut s) = ui.secondary { - s.draw_map.agents.borrow_mut().invalidate_cache(); - } - Some(Transition::Pop) - }, - )))); - } - if let Some(explorer) = RouteExplorer::new(ctx, ui) { return Some(Transition::Push(Box::new(explorer))); } diff --git a/game/src/common/colors.rs b/game/src/common/colors.rs index 624f24c6e4..8cfb5ae7fb 100644 --- a/game/src/common/colors.rs +++ b/game/src/common/colors.rs @@ -160,7 +160,6 @@ pub struct ColorLegend { impl ColorLegend { pub fn new(ctx: &EventCtx, header: Text, rows: Vec<(&str, Color)>) -> ColorLegend { - // TODO add a bg here and stop using prompt? let mut col = vec![ManagedWidget::draw_text(ctx, header)]; let radius = 15.0; diff --git a/game/src/common/minimap.rs b/game/src/common/minimap.rs index 2c5cdc243f..a25902e30f 100644 --- a/game/src/common/minimap.rs +++ b/game/src/common/minimap.rs @@ -1,18 +1,33 @@ -use crate::render::MIN_ZOOM_FOR_DETAIL; +use crate::game::{Transition, WizardState}; +use crate::managed::{Composite, ManagedWidget, Outcome}; +use crate::render::{AgentColorScheme, MIN_ZOOM_FOR_DETAIL}; use crate::ui::UI; -use ezgui::{Color, EventCtx, GfxCtx, ScreenPt, ScreenRectangle}; -use geom::{Distance, Polygon, Pt2D, Ring}; +use ezgui::{ + hotkey, Button, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, RewriteColor, ScreenPt, + ScreenRectangle, Text, +}; +use geom::{Circle, Distance, Polygon, Pt2D, Ring}; +use std::collections::HashMap; pub struct Minimap { dragging: bool, + + controls: VisibilityPanel, } impl Minimap { - pub fn new() -> Minimap { - Minimap { dragging: false } + pub fn new(ctx: &EventCtx, ui: &UI) -> Minimap { + Minimap { + dragging: false, + controls: VisibilityPanel::new(ctx, ui), + } } - pub fn event(&mut self, ui: &mut UI, ctx: &mut EventCtx) { + pub fn event(&mut self, ui: &mut UI, ctx: &mut EventCtx) -> Option { + if let Some(t) = self.controls.event(ctx, ui) { + return Some(t); + } + // TODO duplicate some stuff for now, until we figure out what to cache let square_len = 0.15 * ctx.canvas.window_width; let top_left = ScreenPt::new( @@ -38,7 +53,7 @@ impl Minimap { } else if inner_rect.contains(pt) && ctx.input.left_mouse_button_pressed() { self.dragging = true; } else { - return; + return None; } let percent_x = (pt.x - inner_rect.x1) / (inner_rect.x2 - inner_rect.x1); @@ -53,9 +68,13 @@ impl Minimap { let map_y2 = bounds.min_y + (inner_rect.y2 - inner_rect.y1) / zoom; let map_pt = Pt2D::new(map_x, percent_y * (map_y2 - bounds.min_y)); ctx.canvas.center_on_map_pt(map_pt); + + None } pub fn draw(&self, g: &mut GfxCtx, ui: &UI) { + self.controls.draw(g); + if g.canvas.cam_zoom < MIN_ZOOM_FOR_DETAIL { return; } @@ -154,3 +173,116 @@ fn clamp(x: f64, min: f64, max: f64) -> f64 { x } } + +pub struct VisibilityPanel { + composite: Composite, + enabled: HashMap, +} + +impl VisibilityPanel { + fn make_panel(ctx: &EventCtx, entries: Vec<(String, Color, bool)>) -> Composite { + let radius = 15.0; + let mut col = vec![ManagedWidget::btn_no_cb(Button::text( + Text::from(Line("change")), + Color::INVISIBLE, + Color::ORANGE, + hotkey(Key::Semicolon), + "change agent colorscheme", + ctx, + ))]; + for (label, color, enabled) in entries { + // TODO Blur out when disabled + col.push( + ManagedWidget::row(vec![ + ManagedWidget::btn_no_cb(Button::rectangle_svg( + "assets/tools/visibility.svg", + &format!("show/hide {}", label), + None, + RewriteColor::Change(Color::WHITE, Color::ORANGE), + ctx, + )), + ManagedWidget::draw_batch( + ctx, + GeomBatch::from(vec![( + color, + Circle::new(Pt2D::new(radius, radius), Distance::meters(radius)) + .to_polygon(), + )]), + ), + ManagedWidget::draw_text(ctx, Text::from(Line(label))), + ]) + .centered_cross(), + ); + } + Composite::minimal_size( + ManagedWidget::col(col).bg(Color::grey(0.4)), + ScreenPt::new( + ctx.canvas.window_width - 550.0, + ctx.canvas.window_height - 300.0, + ), + ) + } + + fn new(ctx: &EventCtx, ui: &UI) -> VisibilityPanel { + // TODO take over make_color_legend + let mut rows = Vec::new(); + let mut enabled = HashMap::new(); + for (label, color) in vec![ + ("car", ui.cs.get("unzoomed car")), + ("bike", ui.cs.get("unzoomed bike")), + ("bus", ui.cs.get("unzoomed bus")), + ("pedestrian", ui.cs.get("unzoomed pedestrian")), + ] { + enabled.insert(label.to_string(), true); + rows.push((label.to_string(), color, true)); + } + + VisibilityPanel { + composite: VisibilityPanel::make_panel(ctx, rows), + enabled, + } + } + + fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Option { + match self.composite.event(ctx, ui) { + Some(Outcome::Transition(_)) => unreachable!(), + Some(Outcome::Clicked(x)) => match x.as_ref() { + "change agent colorscheme" => { + return Some(Transition::Push(WizardState::new(Box::new( + |wiz, ctx, ui| { + let (_, acs) = + wiz.wrap(ctx).choose("Which colorscheme for agents?", || { + let mut choices = Vec::new(); + for (acs, name) in AgentColorScheme::all() { + if ui.agent_cs != acs { + choices.push(Choice::new(name, acs)); + } + } + choices + })?; + ui.agent_cs = acs; + ui.agent_cs_legend = acs.make_color_legend(ctx, &ui.cs); + ui.primary.draw_map.agents.borrow_mut().invalidate_cache(); + if let Some(ref mut s) = ui.secondary { + s.draw_map.agents.borrow_mut().invalidate_cache(); + } + Some(Transition::Pop) + }, + )))); + } + x => { + let key = x["show/hide ".len()..].to_string(); + *self.enabled.get_mut(&key).unwrap() = !self.enabled[&key]; + println!("{} is now {}", key, self.enabled[&key]); + } + }, + None => {} + } + + None + } + + fn draw(&self, g: &mut GfxCtx) { + self.composite.draw(g); + } +} diff --git a/game/src/managed.rs b/game/src/managed.rs index 2a3aca0e6f..d2cdbbd247 100644 --- a/game/src/managed.rs +++ b/game/src/managed.rs @@ -80,6 +80,11 @@ impl ManagedWidget { self } + pub fn centered_cross(mut self) -> ManagedWidget { + self.style.align_items = Some(AlignItems::Center); + self + } + pub fn evenly_spaced(mut self) -> ManagedWidget { self.style.justify_content = Some(JustifyContent::SpaceBetween); self diff --git a/game/src/sandbox/mod.rs b/game/src/sandbox/mod.rs index 5c5e2c011a..5ef169e82c 100644 --- a/game/src/sandbox/mod.rs +++ b/game/src/sandbox/mod.rs @@ -47,7 +47,7 @@ impl SandboxMode { common: CommonState::new(), tool_panel: tool_panel(ctx, Some(Box::new(Overlays::change_overlays))), minimap: if mode.has_minimap() { - Some(Minimap::new()) + Some(Minimap::new(ctx, ui)) } else { None }, @@ -57,7 +57,6 @@ impl SandboxMode { vec![ (lctrl(Key::E), "edit mode"), (hotkey(Key::Q), "scoreboard"), - (hotkey(Key::Semicolon), "change agent colorscheme"), (None, "explore a bus route"), ], ctx, @@ -96,7 +95,9 @@ impl State for SandboxMode { ui.recalculate_current_selection(ctx); } if let Some(ref mut m) = self.minimap { - m.event(ui, ctx); + if let Some(t) = m.event(ui, ctx) { + return t; + } } if let Some(t) = self.agent_tools.event(ctx, ui, &mut self.menu) {