map_editor/
load.rs

1use abstio::{Manifest, MapName};
2use raw_map::RawMap;
3use widgetry::tools::{FileLoader, PopupMsg, URLManager};
4use widgetry::{
5    Autocomplete, EventCtx, GfxCtx, Image, Line, Outcome, Panel, State, Transition, Widget,
6};
7
8use crate::app::App;
9use crate::camera::CameraState;
10
11pub struct PickMap {
12    panel: Panel,
13}
14
15impl PickMap {
16    pub fn new_state(ctx: &mut EventCtx) -> Box<dyn State<App>> {
17        let mut entries = Vec::new();
18        for name in MapName::list_all_maps_merged(&Manifest::load()) {
19            entries.push((name.describe(), abstio::path_raw_map(&name)));
20        }
21
22        Box::new(PickMap {
23            panel: Panel::new_builder(Widget::col(vec![
24                Widget::row(vec![
25                    Line("Select a map").small_heading().into_widget(ctx),
26                    ctx.style().btn_close_widget(ctx),
27                ]),
28                Widget::row(vec![
29                    Image::from_path("system/assets/tools/search.svg").into_widget(ctx),
30                    Autocomplete::new_widget(ctx, entries, 20).named("search"),
31                ]),
32            ]))
33            .exact_size_percent(80, 80)
34            .build(ctx),
35        })
36    }
37}
38
39impl State<App> for PickMap {
40    fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition<App> {
41        if let Outcome::Clicked(x) = self.panel.event(ctx) {
42            match x.as_ref() {
43                "close" => {
44                    return Transition::Pop;
45                }
46                _ => unreachable!(),
47            }
48        }
49        if let Some(mut paths) = self.panel.autocomplete_done::<String>("search") {
50            if !paths.is_empty() {
51                return Transition::Push(load_map(
52                    ctx,
53                    paths.remove(0),
54                    app.model.include_bldgs,
55                    None,
56                ));
57            }
58        }
59
60        Transition::Keep
61    }
62
63    fn draw(&self, g: &mut GfxCtx, _: &App) {
64        self.panel.draw(g);
65    }
66}
67
68pub fn load_map(
69    ctx: &mut EventCtx,
70    path: String,
71    include_bldgs: bool,
72    center_camera: Option<String>,
73) -> Box<dyn State<App>> {
74    FileLoader::<App, RawMap>::new_state(
75        ctx,
76        path,
77        Box::new(move |ctx, app, timer, map| match map {
78            Ok(map) => {
79                app.model = crate::model::Model::from_map(ctx, map, include_bldgs, timer);
80
81                if !URLManager::change_camera(
82                    ctx,
83                    center_camera.as_ref(),
84                    &app.model.map.streets.gps_bounds,
85                ) && !app.model.map.name.map.is_empty()
86                {
87                    CameraState::load(ctx, &app.model.map.name);
88                }
89
90                Transition::Clear(vec![crate::app::MainState::new_state(ctx, app)])
91            }
92            Err(err) => Transition::Replace(PopupMsg::new_state(
93                ctx,
94                "Error",
95                vec![
96                    "The format of this file has become out-of-sync with this version of the code."
97                        .to_string(),
98                    "Please file an issue and ask for an update. Sorry for the hassle!".to_string(),
99                    format!("Error: {}", err),
100                ],
101            )),
102        }),
103    )
104}