map_gui/
load.rs

1use abstio::MapName;
2use widgetry::tools::PopupMsg;
3use widgetry::tools::{FileLoader, RawBytes};
4use widgetry::{EventCtx, GfxCtx, State, Transition};
5
6use crate::AppLike;
7
8pub struct MapLoader;
9
10impl MapLoader {
11    pub fn new_state<A: AppLike + 'static>(
12        ctx: &mut EventCtx,
13        app: &A,
14        name: MapName,
15        on_load: Box<dyn FnOnce(&mut EventCtx, &mut A) -> Transition<A>>,
16    ) -> Box<dyn State<A>> {
17        if app.map().get_name() == &name {
18            return Box::new(MapAlreadyLoaded {
19                on_load: Some(on_load),
20            });
21        }
22
23        MapLoader::force_reload(ctx, name, on_load)
24    }
25
26    /// Even if the current map name matches, still reload.
27    pub fn force_reload<A: AppLike + 'static>(
28        ctx: &mut EventCtx,
29        name: MapName,
30        on_load: Box<dyn FnOnce(&mut EventCtx, &mut A) -> Transition<A>>,
31    ) -> Box<dyn State<A>> {
32        // TODO Generalize this more, maybe with some kind of country code -> font config
33        if let Some(extra_font) = match name.city.country.as_ref() {
34            "hk" | "jp" | "tw" => Some("NotoSerifCJKtc-Regular.otf"),
35            "il" => Some("NotoSansHebrew-Regular.ttf"),
36            "ir" | "ly" => Some("NotoSansArabic-Regular.ttf"),
37            "kr" => Some("NotoSansKR-Regular.ttf"),
38            _ => None,
39        } {
40            if !ctx.is_font_loaded(extra_font) {
41                return FileLoader::<A, RawBytes>::new_state(
42                    ctx,
43                    abstio::path(format!("system/extra_fonts/{}", extra_font)),
44                    Box::new(move |ctx, app, _, bytes| match bytes {
45                        Ok(bytes) => {
46                            ctx.load_font(extra_font, bytes.0);
47                            Transition::Replace(MapLoader::new_state(ctx, app, name, on_load))
48                        }
49                        Err(err) => Transition::Replace(PopupMsg::new_state(
50                            ctx,
51                            "Error",
52                            vec![format!("Couldn't load {}", extra_font), err.to_string()],
53                        )),
54                    }),
55                );
56            }
57        }
58
59        FileLoader::<A, map_model::Map>::new_state(
60            ctx,
61            name.path(),
62            Box::new(move |ctx, app, timer, map| {
63                match map {
64                    Ok(mut map) => {
65                        // Kind of a hack. We can't generically call Map::new with the FileLoader.
66                        map.map_loaded_directly(timer);
67
68                        app.map_switched(ctx, map, timer);
69
70                        (on_load)(ctx, app)
71                    }
72                    Err(err) => Transition::Replace(PopupMsg::new_state(
73                        ctx,
74                        "Error",
75                        vec![
76                            format!("Couldn't load {}", name.describe()),
77                            err.to_string(),
78                        ],
79                    )),
80                }
81            }),
82        )
83    }
84}
85
86struct MapAlreadyLoaded<A: AppLike> {
87    on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut A) -> Transition<A>>>,
88}
89impl<A: AppLike + 'static> State<A> for MapAlreadyLoaded<A> {
90    fn event(&mut self, ctx: &mut EventCtx, app: &mut A) -> Transition<A> {
91        (self.on_load.take().unwrap())(ctx, app)
92    }
93    fn draw(&self, _: &mut GfxCtx, _: &A) {}
94}