Clean up CityPicker code using DrawWithTooltips

This commit is contained in:
Dustin Carlino 2021-07-17 17:13:35 -07:00
parent 942f2292fc
commit 3d355f720c
2 changed files with 47 additions and 59 deletions

View File

@ -208,6 +208,24 @@ impl<K: Clone + PartialEq, V> VecMap<K, V> {
self.inner.push((key, ctor())); self.inner.push((key, ctor()));
&mut self.inner.last_mut().unwrap().1 &mut self.inner.last_mut().unwrap().1
} }
/// Doesn't dedupe
pub fn push(&mut self, key: K, value: V) {
self.inner.push((key, value));
}
pub fn get(&self, key: &K) -> Option<&V> {
for (k, v) in &self.inner {
if k == key {
return Some(v);
}
}
None
}
pub fn len(&self) -> usize {
self.inner.len()
}
} }
impl<K: Clone + PartialEq, V> Default for VecMap<K, V> { impl<K: Clone + PartialEq, V> Default for VecMap<K, V> {

View File

@ -1,11 +1,12 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use abstio::{CityName, Manifest, MapName}; use abstio::{CityName, Manifest, MapName};
use geom::{Distance, Percent, Polygon, Pt2D}; use abstutil::VecMap;
use geom::{Distance, Percent};
use map_model::City; use map_model::City;
use widgetry::{ use widgetry::{
Autocomplete, Color, ControlState, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Image, Key, Autocomplete, ControlState, DrawBaselayer, DrawWithTooltips, EventCtx, GeomBatch, GfxCtx,
Line, Outcome, Panel, RewriteColor, ScreenPt, State, Text, TextExt, Transition, Widget, Image, Key, Line, Outcome, Panel, RewriteColor, State, Text, TextExt, Transition, Widget,
}; };
use crate::load::{FileLoader, MapLoader}; use crate::load::{FileLoader, MapLoader};
@ -16,9 +17,6 @@ use crate::AppLike;
/// Lets the player switch maps. /// Lets the player switch maps.
pub struct CityPicker<A: AppLike> { pub struct CityPicker<A: AppLike> {
panel: Panel, panel: Panel,
// In untranslated screen-space
districts: Vec<(MapName, Color, Polygon)>,
selected: Option<usize>,
// Wrapped in an Option just to make calling from event() work. // Wrapped in an Option just to make calling from event() work.
on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut A) -> Transition<A>>>, on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut A) -> Transition<A>>>,
} }
@ -46,14 +44,13 @@ impl<A: AppLike + 'static> CityPicker<A> {
)), )),
Box::new(move |ctx, app, _, maybe_city| { Box::new(move |ctx, app, _, maybe_city| {
// If city.bin exists, use it to draw the district map. // If city.bin exists, use it to draw the district map.
let mut batch = GeomBatch::new(); let district_picker = if let Ok(city) = maybe_city {
let mut districts = Vec::new();
if let Ok(city) = maybe_city {
let bounds = city.boundary.get_bounds(); let bounds = city.boundary.get_bounds();
let zoom = (0.8 * ctx.canvas.window_width / bounds.width()) let zoom = (0.8 * ctx.canvas.window_width / bounds.width())
.min(0.8 * ctx.canvas.window_height / bounds.height()); .min(0.8 * ctx.canvas.window_height / bounds.height());
let mut batch = GeomBatch::new();
batch.push(app.cs().map_background.clone(), city.boundary); batch.push(app.cs().map_background.clone(), city.boundary);
for (area_type, polygon) in city.areas { for (area_type, polygon) in city.areas {
batch.push(DrawArea::fill(area_type, app.cs()), polygon); batch.push(DrawArea::fill(area_type, app.cs()), polygon);
@ -62,15 +59,33 @@ impl<A: AppLike + 'static> CityPicker<A> {
// If somebody has just generated a new map somewhere with an existing // If somebody has just generated a new map somewhere with an existing
// city.bin, but hasn't updated city.bin yet, that new map will be invisible on // city.bin, but hasn't updated city.bin yet, that new map will be invisible on
// the city-wide diagram. // the city-wide diagram.
let mut tooltips = Vec::new();
let mut colors = VecMap::new();
for (name, polygon) in city.districts { for (name, polygon) in city.districts {
if &name != app.map().get_name() { if &name != app.map().get_name() {
let color = app.cs().rotating_color_agents(districts.len()); let color = app.cs().rotating_color_agents(colors.len());
batch.push(color, polygon.to_outline(Distance::meters(200.0)).unwrap()); batch.push(color, polygon.to_outline(Distance::meters(200.0)).unwrap());
districts.push((name, color, polygon.scale(zoom))); let polygon = polygon.scale(zoom);
tooltips.push((
polygon.clone(),
Text::from(nice_map_name(&name)),
Some(name.path()),
));
colors.push(polygon, color);
} }
} }
batch = batch.scale(zoom); DrawWithTooltips::new_widget(
} ctx,
batch.scale(zoom),
tooltips,
Box::new(move |poly| {
let color = colors.get(poly).unwrap();
GeomBatch::from(vec![(color.alpha(0.5), poly.clone())])
}),
)
} else {
Widget::nothing()
};
// Use the filesystem to list the buttons on the side. // Use the filesystem to list the buttons on the side.
// (There's no point in listing these from city.bin if it exists -- if somebody // (There's no point in listing these from city.bin if it exists -- if somebody
@ -129,8 +144,6 @@ impl<A: AppLike + 'static> CityPicker<A> {
); );
Transition::Replace(Box::new(CityPicker { Transition::Replace(Box::new(CityPicker {
districts,
selected: None,
on_load: Some(on_load), on_load: Some(on_load),
panel: Panel::new_builder(Widget::col(vec![ panel: Panel::new_builder(Widget::col(vec![
Widget::row(vec![ Widget::row(vec![
@ -139,7 +152,7 @@ impl<A: AppLike + 'static> CityPicker<A> {
]), ]),
Widget::row(vec![ Widget::row(vec![
Widget::col(other_places).centered_vert(), Widget::col(other_places).centered_vert(),
batch.into_widget(ctx).named("picker"), district_picker,
Widget::col(this_city).centered_vert(), Widget::col(this_city).centered_vert(),
]), ]),
"Don't see the place you want?".text_widget(ctx), "Don't see the place you want?".text_widget(ctx),
@ -209,34 +222,6 @@ impl<A: AppLike + 'static> State<A> for CityPicker<A> {
} }
} }
if ctx.redo_mouseover() {
self.selected = None;
if let Some(cursor) = ctx.canvas.get_cursor_in_screen_space() {
let rect = self.panel.rect_of("picker");
if rect.contains(cursor) {
let pt = Pt2D::new(cursor.x - rect.x1, cursor.y - rect.y1);
for (idx, (_, _, poly)) in self.districts.iter().enumerate() {
if poly.contains_pt(pt) {
self.selected = Some(idx);
break;
}
}
} else if let Some(btn) = self.panel.currently_hovering() {
for (idx, (name, _, _)) in self.districts.iter().enumerate() {
if &name.path() == btn {
self.selected = Some(idx);
break;
}
}
}
}
}
if let Some(idx) = self.selected {
if ctx.normal_left_click() {
return chose_city(ctx, app, self.districts[idx].0.clone(), &mut self.on_load);
}
}
Transition::Keep Transition::Keep
} }
@ -247,21 +232,6 @@ impl<A: AppLike + 'static> State<A> for CityPicker<A> {
fn draw(&self, g: &mut GfxCtx, app: &A) { fn draw(&self, g: &mut GfxCtx, app: &A) {
grey_out_map(g, app); grey_out_map(g, app);
self.panel.draw(g); self.panel.draw(g);
if let Some(idx) = self.selected {
let (name, color, poly) = &self.districts[idx];
let rect = self.panel.rect_of("picker");
g.fork(
Pt2D::new(0.0, 0.0),
ScreenPt::new(rect.x1, rect.y1),
1.0,
None,
);
g.draw_polygon(color.alpha(0.5), poly.clone());
g.unfork();
g.draw_mouse_tooltip(Text::from(nice_map_name(name)));
}
} }
} }