1use abstutil::Timer;
5use geom::{LonLat, Percent};
6use map_gui::colors::ColorSchemeChoice;
7use map_gui::tools::CityPicker;
8use map_gui::AppLike;
9use widgetry::tools::ChooseSomething;
10use widgetry::{Choice, EventCtx, Key, Line, Panel, SimpleState, State, Widget};
11
12use crate::app::{App, Transition};
13
14mod collisions;
15pub mod compare_counts;
16mod destinations;
17pub mod kml;
18mod polygon;
19mod scenario;
20mod story;
21
22pub struct DevToolsMode;
23
24impl DevToolsMode {
25 pub fn new_state(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
26 app.change_color_scheme(ctx, ColorSchemeChoice::DayMode);
27
28 let panel = Panel::new_builder(Widget::col(vec![
29 Widget::row(vec![
30 Line("Advanced tools").small_heading().into_widget(ctx),
31 ctx.style().btn_close_widget(ctx),
32 ]),
33 map_gui::tools::change_map_btn(ctx, app),
34 Widget::custom_row(vec![
35 ctx.style()
36 .btn_outline
37 .text("edit a polygon")
38 .hotkey(Key::E)
39 .build_def(ctx),
40 ctx.style()
41 .btn_outline
42 .text("draw a polygon")
43 .hotkey(Key::P)
44 .build_def(ctx),
45 ctx.style()
46 .btn_outline
47 .text("load scenario")
48 .hotkey(Key::W)
49 .build_def(ctx),
50 ctx.style()
51 .btn_outline
52 .text("view KML")
53 .hotkey(Key::K)
54 .build_def(ctx),
55 ctx.style()
56 .btn_outline
57 .text("story maps")
58 .hotkey(Key::S)
59 .build_def(ctx),
60 if abstio::file_exists(app.primary.map.get_city_name().input_path("collisions.bin"))
61 {
62 ctx.style()
63 .btn_outline
64 .text("collisions")
65 .hotkey(Key::C)
66 .build_def(ctx)
67 } else {
68 Widget::nothing()
69 },
70 ])
71 .flex_wrap(ctx, Percent::int(60)),
72 Widget::row(vec![
73 ctx.style()
74 .btn_solid_primary
75 .text("OpenStreetMap viewer")
76 .build_def(ctx),
77 if cfg!(not(target_arch = "wasm32")) {
78 ctx.style()
79 .btn_solid_primary
80 .text("Parking mapper")
81 .build_def(ctx)
82 } else {
83 Widget::nothing()
84 },
85 if abstio::file_exists(abstio::path_raw_map(app.primary.map.get_name())) {
86 ctx.style()
87 .btn_solid_primary
88 .text("RawMap editor")
89 .build_def(ctx)
90 } else {
91 Widget::nothing()
92 },
93 ]),
94 ]))
95 .build(ctx);
96 <dyn SimpleState<_>>::new_state(panel, Box::new(DevToolsMode))
97 }
98}
99
100impl SimpleState<App> for DevToolsMode {
101 fn on_click(
102 &mut self,
103 ctx: &mut EventCtx,
104 app: &mut App,
105 x: &str,
106 _: &mut Panel,
107 ) -> Transition {
108 match x {
109 "close" => Transition::Pop,
110 "edit a polygon" => {
111 Transition::Push(ChooseSomething::new_state(
112 ctx,
113 "Choose a polygon",
114 abstio::list_dir(abstio::path(format!(
118 "../importer/config/{}/{}",
119 app.primary.map.get_city_name().country,
120 app.primary.map.get_city_name().city
121 )))
122 .into_iter()
123 .filter(|path| path.ends_with(".geojson"))
124 .map(|path| Choice::new(abstutil::basename(&path), path))
125 .collect(),
126 Box::new(|path, ctx, app| match LonLat::read_geojson_polygon(&path) {
127 Ok(pts) => Transition::Replace(polygon::PolygonEditor::new_state(
128 ctx,
129 app,
130 abstutil::basename(path),
131 pts,
132 )),
133 Err(err) => {
134 println!("Bad polygon {}: {}", path, err);
135 Transition::Pop
136 }
137 }),
138 ))
139 }
140 "draw a polygon" => Transition::Push(polygon::PolygonEditor::new_state(
141 ctx,
142 app,
143 "name goes here".to_string(),
144 Vec::new(),
145 )),
146 "load scenario" => Transition::Push(ChooseSomething::new_state(
147 ctx,
148 "Choose a scenario",
149 Choice::strings(abstio::list_all_objects(abstio::path_all_scenarios(
150 app.primary.map.get_name(),
151 ))),
152 Box::new(|s, ctx, app| {
153 let scenario = abstio::read_binary(
154 abstio::path_scenario(app.primary.map.get_name(), &s),
155 &mut Timer::throwaway(),
156 );
157 Transition::Replace(scenario::ScenarioManager::new_state(scenario, ctx, app))
158 }),
159 )),
160 "view KML" => Transition::Push(kml::ViewKML::new_state(ctx, app, None)),
161 "story maps" => Transition::Push(story::StoryMapEditor::new_state(ctx)),
162 "collisions" => Transition::Push(collisions::CollisionsViewer::new_state(ctx, app)),
163 "OpenStreetMap viewer" => {
164 map_gui::tools::Executable::OSMViewer.replace_process(ctx, app, vec![])
165 }
166 "Parking mapper" => {
167 map_gui::tools::Executable::ParkingMapper.replace_process(ctx, app, vec![])
168 }
169 "RawMap editor" => {
170 map_gui::tools::Executable::RawMapEditor.replace_process(ctx, app, vec![])
171 }
172 "change map" => Transition::Push(CityPicker::new_state(
173 ctx,
174 app,
175 Box::new(|ctx, app| {
176 Transition::Multi(vec![
177 Transition::Pop,
178 Transition::Replace(DevToolsMode::new_state(ctx, app)),
179 ])
180 }),
181 )),
182 _ => unreachable!(),
183 }
184 }
185}