mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 00:12:55 +03:00
robustify city picker
This commit is contained in:
parent
239bf74121
commit
8b7d93c64e
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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};
|
||||||
|
@ -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};
|
||||||
|
Loading…
Reference in New Issue
Block a user