Make the city picker handle 100's of maps slightly better, by splitting out into a separate list with autocomplete

This commit is contained in:
Dustin Carlino 2020-10-28 16:08:09 -07:00
parent 304e723d77
commit c8b565e57b

View File

@ -1,13 +1,14 @@
use geom::{Distance, Polygon, Pt2D};
use abstutil::prettyprint_usize;
use geom::{Distance, Percent, Polygon, Pt2D};
use map_model::City;
use widgetry::{
Btn, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Line, Outcome, Panel, ScreenPt, State,
Text, Widget,
Autocomplete, Btn, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Line, Outcome, Panel,
ScreenPt, State, Text, TextExt, Widget,
};
use crate::app::App;
use crate::game::Transition;
use crate::helpers::{grey_out_map, nice_map_name};
use crate::helpers::{grey_out_map, nice_map_name, open_browser};
use crate::load::MapLoader;
use crate::render::DrawArea;
@ -62,6 +63,7 @@ impl CityPicker {
let mut other_cities = vec![Line("Other cities").draw(ctx)];
let mut this_city = vec![];
let mut more_cities = 0;
for name in abstutil::list_all_objects(abstutil::path_all_maps()) {
if let Some((_, color, _)) = regions.iter().find(|(n, _, _)| &name == n) {
let btn = Btn::txt(&name, Text::from(Line(nice_map_name(&name)).fg(*color)))
@ -71,33 +73,61 @@ impl CityPicker {
} else {
btn.build_def(ctx, None)
});
} else {
} else if other_cities.len() < 10 {
other_cities.push(
Btn::txt(&name, Text::from(Line(nice_map_name(&name))))
.tooltip(Text::new())
.build_def(ctx, None),
);
} else {
more_cities += 1;
}
}
if more_cities > 0 {
other_cities.push(
Btn::text_bg2(format!("{} more cities", prettyprint_usize(more_cities))).build(
ctx,
"more cities",
None,
),
);
}
if !this_city.is_empty() {
this_city.insert(
0,
format!("More regions in {}", app.primary.map.get_city_name()).draw_text(ctx),
);
}
Box::new(CityPicker {
regions,
selected: None,
on_load: Some(on_load),
panel: Panel::new(
Widget::col(vec![
Widget::row(vec![
Line("Select a region").small_heading().draw(ctx),
Btn::close(ctx),
]),
Widget::row(vec![
Widget::col(other_cities).centered_vert(),
Widget::draw_batch(ctx, batch).named("picker"),
Widget::col(this_city).centered_vert(),
]),
])
.outline(2.0, Color::WHITE),
)
panel: Panel::new(Widget::col(vec![
Widget::row(vec![
Line("Select a region").small_heading().draw(ctx),
Btn::close(ctx),
]),
Widget::row(vec![
Widget::col(other_cities).centered_vert(),
Widget::draw_batch(ctx, batch).named("picker"),
Widget::col(this_city).centered_vert(),
]),
Widget::custom_row(vec![
"Don't see the city you want?"
.draw_text(ctx)
.centered_vert(),
Btn::plaintext_custom(
"import new city",
Text::from(
Line("Import a new city into A/B Street")
.fg(Color::hex("#4CA4E5"))
.underlined(),
),
)
.build_def(ctx, None),
]),
]))
.build(ctx),
})
}
@ -110,6 +140,17 @@ impl State<App> for CityPicker {
"close" => {
return Transition::Pop;
}
"more cities" => {
return Transition::Replace(AllCityPicker::new(
ctx,
self.on_load.take().unwrap(),
));
}
"import new city" => {
open_browser(
"https://dabreegster.github.io/abstreet/howto/new_city.html".to_string(),
);
}
name => {
return Transition::Replace(MapLoader::new(
ctx,
@ -186,3 +227,89 @@ impl State<App> for CityPicker {
}
}
}
struct AllCityPicker {
panel: Panel,
// Wrapped in an Option just to make calling from event() work.
on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>>,
}
impl AllCityPicker {
fn new(
ctx: &mut EventCtx,
on_load: Box<dyn FnOnce(&mut EventCtx, &mut App) -> Transition>,
) -> Box<dyn State<App>> {
let mut autocomplete_entries = Vec::new();
let mut buttons = Vec::new();
for name in abstutil::list_all_objects(abstutil::path_all_maps()) {
buttons.push(
Btn::text_fg(&name)
.build_def(ctx, None)
.margin_right(10)
.margin_below(10),
);
autocomplete_entries.push((name.clone(), name));
}
Box::new(AllCityPicker {
on_load: Some(on_load),
panel: Panel::new(Widget::col(vec![
Widget::row(vec![
Line("Select a region").small_heading().draw(ctx),
Btn::close(ctx),
]),
Widget::row(vec![
Widget::draw_svg(ctx, "system/assets/tools/search.svg"),
Autocomplete::new(ctx, autocomplete_entries).named("search"),
])
.padding(8),
Widget::custom_row(buttons).flex_wrap(ctx, Percent::int(70)),
]))
.exact_size_percent(80, 80)
.build(ctx),
})
}
}
impl State<App> for AllCityPicker {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
match self.panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"close" => {
return Transition::Pop;
}
name => {
return Transition::Replace(MapLoader::new(
ctx,
app,
name.to_string(),
self.on_load.take().unwrap(),
));
}
},
_ => {}
}
if let Some(mut names) = self.panel.autocomplete_done::<String>("search") {
if !names.is_empty() {
return Transition::Replace(MapLoader::new(
ctx,
app,
names.remove(0),
self.on_load.take().unwrap(),
));
}
}
Transition::Keep
}
fn draw_baselayer(&self) -> DrawBaselayer {
DrawBaselayer::PreviousState
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
grey_out_map(g, app);
self.panel.draw(g);
}
}