robustify city picker

This commit is contained in:
Dustin Carlino 2020-05-28 17:17:50 -07:00
parent 239bf74121
commit 8b7d93c64e
5 changed files with 85 additions and 38 deletions

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
svg, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, MultiKey, Outcome, svg, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, MultiKey, Outcome,
RewriteColor, ScreenDims, ScreenPt, Text, Widget, WidgetImpl, WidgetOutput, RewriteColor, ScreenDims, ScreenPt, Text, TextSpan, Widget, WidgetImpl, WidgetOutput,
}; };
use geom::Polygon; use geom::Polygon;
@ -142,6 +142,11 @@ impl Btn {
BtnBuilder::TextFG(label.clone(), Text::from(Line(label)), None) BtnBuilder::TextFG(label.clone(), Text::from(Line(label)), None)
} }
pub fn text_fg_line<I: Into<String>>(label: I, line: TextSpan) -> BtnBuilder {
let label = label.into();
BtnBuilder::TextFG(label.clone(), Text::from(line), None)
}
pub fn text_bg<I: Into<String>>( pub fn text_bg<I: Into<String>>(
label: I, label: I,
text: Text, text: Text,

View File

@ -1,15 +1,14 @@
use crate::app::App; use crate::app::App;
use crate::game::{DrawBaselayer, State, Transition}; use crate::game::{DrawBaselayer, State, Transition};
use crate::helpers::nice_map_name; use crate::helpers::nice_map_name;
use crate::render::DrawArea;
use ezgui::{ use ezgui::{
hotkey, Btn, Color, Composite, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, ScreenPt, Text, hotkey, Btn, Color, Composite, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, ScreenPt, Text,
Widget, Widget,
}; };
use geom::{Distance, Polygon, Pt2D}; use geom::{Distance, Polygon, Pt2D};
use map_model::{AreaType, City}; use map_model::City;
// TODO Also include text buttons
// TODO Handle other cities
pub struct CityPicker { pub struct CityPicker {
composite: Composite, composite: Composite,
// In untranslated screen-space // In untranslated screen-space
@ -21,40 +20,64 @@ pub struct CityPicker {
impl CityPicker { impl CityPicker {
pub fn new( pub fn new(
ctx: &mut EventCtx, ctx: &mut EventCtx,
app: &App, app: &mut App,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>, on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>,
) -> Box<dyn State> { ) -> Box<dyn State> {
// TODO Handle if the city doesn't exist app.primary.current_selection = None;
let city: City = abstutil::read_binary(
let mut batch = GeomBatch::new();
let mut regions = Vec::new();
if let Ok(city) = abstutil::maybe_read_binary::<City>(
format!( format!(
"../data/system/cities/{}.bin", "../data/system/cities/{}.bin",
app.primary.map.get_city_name() app.primary.map.get_city_name()
), ),
&mut abstutil::Timer::throwaway(), &mut abstutil::Timer::throwaway(),
); ) {
let bounds = city.boundary.get_bounds();
let zoom = (0.8 * ctx.canvas.window_width / bounds.width())
.min(0.8 * ctx.canvas.window_height / bounds.height());
let bounds = city.boundary.get_bounds(); batch.push(app.cs.map_background, city.boundary);
let zoom = (0.8 * ctx.canvas.window_width / bounds.width()) for (area_type, polygon) in city.areas {
.min(0.8 * ctx.canvas.window_height / bounds.height()); batch.push(DrawArea::color(area_type, &app.cs), polygon);
}
let mut batch = GeomBatch::new(); for (name, polygon) in city.regions {
batch.push(app.cs.map_background, city.boundary); let color = app.cs.rotating_color_agents(regions.len());
for (area_type, polygon) in city.areas { if &name == app.primary.map.get_name() {
// TODO Refactor batch.push(color.alpha(0.5), polygon.clone());
let color = match area_type { } else {
AreaType::Park => app.cs.grass, batch.push(color, polygon.to_outline(Distance::meters(200.0)));
AreaType::Water => app.cs.water, }
AreaType::PedestrianIsland => Color::grey(0.3), regions.push((name, color, polygon.scale(zoom)));
AreaType::Island => app.cs.map_background, }
}; batch = batch.scale(zoom);
batch.push(color, polygon);
} }
let mut regions = Vec::new(); let mut other_cities = vec![Line("Other cities").draw(ctx).margin_below(10)];
for (name, polygon) in city.regions { let mut this_city = vec![];
let color = app.cs.rotating_color_agents(regions.len()); for name in abstutil::list_all_objects(abstutil::path_all_maps()) {
batch.push(color, polygon.to_outline(Distance::meters(200.0))); if let Some((_, color, _)) = regions.iter().find(|(n, _, _)| &name == n) {
regions.push((name, color, polygon.scale(zoom))); let btn = Btn::text_fg_line(&name, Line(nice_map_name(&name)).fg(*color))
.tooltip(Text::new());
this_city.push(
if &name == app.primary.map.get_name() {
btn.inactive(ctx)
} else {
btn.build_def(ctx, None)
}
.margin_below(5),
);
} else {
other_cities.push(
Btn::text_fg(nice_map_name(&name))
.tooltip(Text::new())
.build(ctx, name, None)
.margin_below(5),
);
}
} }
Box::new(CityPicker { Box::new(CityPicker {
@ -64,12 +87,16 @@ impl CityPicker {
composite: Composite::new( composite: Composite::new(
Widget::col(vec![ Widget::col(vec![
Widget::row(vec![ Widget::row(vec![
Line("Click a region").small_heading().draw(ctx), Line("Select a region").small_heading().draw(ctx),
Btn::plaintext("X") Btn::plaintext("X")
.build(ctx, "close", hotkey(Key::Escape)) .build(ctx, "close", hotkey(Key::Escape))
.align_right(), .align_right(),
]), ]),
Widget::draw_batch(ctx, batch.scale(zoom)).named("picker"), Widget::row(vec![
Widget::col(other_cities).centered_vert(),
Widget::draw_batch(ctx, batch).named("picker"),
Widget::col(this_city).centered_vert(),
]),
]) ])
.bg(app.cs.panel_bg) .bg(app.cs.panel_bg)
.outline(2.0, Color::WHITE) .outline(2.0, Color::WHITE)
@ -87,7 +114,12 @@ impl State for CityPicker {
"close" => { "close" => {
return Transition::Pop; return Transition::Pop;
} }
_ => unreachable!(), name => {
return ctx.loading_screen("switch map", |ctx, _| {
app.switch_map(ctx, abstutil::path_map(name));
(self.on_load)(ctx, app)
});
}
}, },
None => {} None => {}
} }
@ -98,8 +130,15 @@ impl State for CityPicker {
let rect = self.composite.rect_of("picker"); let rect = self.composite.rect_of("picker");
if rect.contains(cursor) { if rect.contains(cursor) {
let pt = Pt2D::new(cursor.x - rect.x1, cursor.y - rect.y1); let pt = Pt2D::new(cursor.x - rect.x1, cursor.y - rect.y1);
for (idx, (_, _, poly)) in self.regions.iter().enumerate() { for (idx, (name, _, poly)) in self.regions.iter().enumerate() {
if poly.contains_pt(pt) { if name != app.primary.map.get_name() && poly.contains_pt(pt) {
self.selected = Some(idx);
break;
}
}
} else if let Some(btn) = self.composite.currently_hovering() {
for (idx, (name, _, _)) in self.regions.iter().enumerate() {
if name != app.primary.map.get_name() && name == btn {
self.selected = Some(idx); self.selected = Some(idx);
break; break;
} }

View File

@ -12,14 +12,17 @@ pub struct DrawArea {
impl DrawArea { impl DrawArea {
pub fn new(area: &Area, cs: &ColorScheme, all_areas: &mut GeomBatch) -> DrawArea { pub fn new(area: &Area, cs: &ColorScheme, all_areas: &mut GeomBatch) -> DrawArea {
let color = match area.area_type { all_areas.push(DrawArea::color(area.area_type, cs), area.polygon.clone());
DrawArea { id: area.id }
}
pub fn color(area_type: AreaType, cs: &ColorScheme) -> Color {
match area_type {
AreaType::Park => cs.grass, AreaType::Park => cs.grass,
AreaType::Water => cs.water, AreaType::Water => cs.water,
AreaType::PedestrianIsland => Color::grey(0.3), AreaType::PedestrianIsland => Color::grey(0.3),
AreaType::Island => cs.map_background, AreaType::Island => cs.map_background,
}; }
all_areas.push(color, area.polygon.clone());
DrawArea { id: area.id }
} }
} }

View File

@ -1,13 +1,12 @@
use crate::app::{App, Flags}; use crate::app::{App, Flags};
use crate::colors::ColorScheme; use crate::colors::ColorScheme;
use crate::helpers::ID; use crate::helpers::ID;
use crate::render::area::DrawArea;
use crate::render::building::DrawBuilding; use crate::render::building::DrawBuilding;
use crate::render::bus_stop::DrawBusStop; use crate::render::bus_stop::DrawBusStop;
use crate::render::intersection::DrawIntersection; use crate::render::intersection::DrawIntersection;
use crate::render::lane::DrawLane; use crate::render::lane::DrawLane;
use crate::render::road::DrawRoad; use crate::render::road::DrawRoad;
use crate::render::{draw_vehicle, DrawPedCrowd, DrawPedestrian, Renderable}; use crate::render::{draw_vehicle, DrawArea, DrawPedCrowd, DrawPedestrian, Renderable};
use aabb_quadtree::QuadTree; use aabb_quadtree::QuadTree;
use abstutil::Timer; use abstutil::Timer;
use ezgui::{Color, Drawable, EventCtx, GeomBatch, GfxCtx, Prerender}; use ezgui::{Color, Drawable, EventCtx, GeomBatch, GfxCtx, Prerender};

View File

@ -14,6 +14,7 @@ mod turn;
use crate::app::App; use crate::app::App;
use crate::colors::ColorScheme; use crate::colors::ColorScheme;
use crate::helpers::ID; use crate::helpers::ID;
pub use crate::render::area::DrawArea;
use crate::render::bike::DrawBike; use crate::render::bike::DrawBike;
use crate::render::car::DrawCar; use crate::render::car::DrawCar;
pub use crate::render::intersection::{calculate_corners, DrawIntersection}; pub use crate::render::intersection::{calculate_corners, DrawIntersection};