1use std::collections::BTreeMap;
2
3use crate::ID;
4use geom::Pt2D;
5use map_gui::tools::grey_out_map;
6use map_model::{AreaID, BuildingID, IntersectionID, LaneID, ParkingLotID, RoadID, TransitRouteID};
7use sim::{PedestrianID, PersonID, TripID};
8use widgetry::tools::PopupMsg;
9use widgetry::{
10 EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, TextBox, TextExt, Warper, Widget,
11};
12
13use crate::app::{App, PerMap, Transition};
14use crate::info::{OpenTrip, Tab};
15use crate::sandbox::SandboxMode;
16
17const WARP_TO_CAM_ZOOM: f64 = 10.0;
18
19pub struct Warping {
20 warper: Warper,
21 id: Option<ID>,
22}
23
24impl Warping {
25 pub fn new_state(
26 ctx: &EventCtx,
27 pt: Pt2D,
28 target_cam_zoom: Option<f64>,
29 id: Option<ID>,
30 primary: &mut PerMap,
31 ) -> Box<dyn State<App>> {
32 primary.last_warped_from = Some((ctx.canvas.center_to_map_pt(), ctx.canvas.cam_zoom));
33 Box::new(Warping {
34 warper: Warper::new(ctx, pt, target_cam_zoom),
35 id,
36 })
37 }
38}
39
40impl State<App> for Warping {
41 fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
42 if self.warper.event(ctx) {
43 Transition::Keep
44 } else if let Some(id) = self.id.clone() {
45 Transition::Multi(vec![
46 Transition::Pop,
47 Transition::ModifyState(Box::new(move |state, ctx, app| {
48 if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
50 let mut actions = s.contextual_actions();
51 s.controls.common.as_mut().unwrap().launch_info_panel(
52 ctx,
53 app,
54 Tab::from_id(app, id),
55 &mut actions,
56 );
57 }
58 })),
59 ])
60 } else {
61 Transition::Pop
62 }
63 }
64
65 fn draw(&self, _: &mut GfxCtx, _: &App) {}
66}
67
68pub struct DebugWarp {
69 panel: Panel,
70}
71
72impl DebugWarp {
73 pub fn new_state(ctx: &mut EventCtx) -> Box<dyn State<App>> {
74 let c = ctx.style().text_hotkey_color;
75 Box::new(DebugWarp {
76 panel: Panel::new_builder(Widget::col(vec![
77 Widget::row(vec![
78 Line("Warp to an object by ID")
79 .small_heading()
80 .into_widget(ctx),
81 ctx.style().btn_close_widget(ctx),
82 ]),
83 "Example: r42 is Road #42".text_widget(ctx),
84 Text::from_all(vec![
93 Line("r").fg(c),
94 Line("oad, "),
95 Line("l").fg(c),
96 Line("ane, "),
97 Line("i").fg(c),
98 Line("ntersection, "),
99 Line("b").fg(c),
100 Line("uilding, "),
101 Line("p").fg(c),
102 Line("edestrian, "),
103 Line("c").fg(c),
104 Line("ar, "),
105 Line("t").fg(c),
106 Line("rip, "),
107 Line("P").fg(c),
108 Line("erson, "),
109 Line("R").fg(c),
110 Line("oute, parking "),
111 Line("L").fg(c),
112 Line("ot"),
113 ])
114 .into_widget(ctx),
115 Text::from_all(vec![
116 Line("Or "),
117 Line("j").fg(c),
118 Line("ump to the previous position"),
119 ])
120 .into_widget(ctx),
121 TextBox::default_widget(ctx, "input", String::new()),
122 ctx.style()
123 .btn_outline
124 .text("Go!")
125 .hotkey(Key::Enter)
126 .build_def(ctx),
127 ]))
128 .build(ctx),
129 })
130 }
131}
132
133impl State<App> for DebugWarp {
134 fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
135 match self.panel.event(ctx) {
136 Outcome::Clicked(x) => match x.as_ref() {
137 "close" => Transition::Pop,
138 "Go!" => {
139 let input = self.panel.text_box("input");
140 warp_to_id(ctx, app, &input)
141 }
142 _ => unreachable!(),
143 },
144 _ => Transition::Keep,
145 }
146 }
147
148 fn draw(&self, g: &mut GfxCtx, app: &App) {
149 grey_out_map(g, app);
150 self.panel.draw(g);
151 }
152}
153
154pub fn warp_to_id(ctx: &mut EventCtx, app: &mut App, input: &str) -> Transition {
155 if let Some(t) = inner_warp_to_id(ctx, app, input) {
156 t
157 } else {
158 Transition::Replace(PopupMsg::new_state(
159 ctx,
160 "Bad warp ID",
161 vec![format!("{} isn't a valid ID", input)],
162 ))
163 }
164}
165
166pub fn inner_warp_to_id(ctx: &mut EventCtx, app: &mut App, line: &str) -> Option<Transition> {
167 if line.is_empty() {
168 return None;
169 }
170 if line == "j" {
171 if let Some((pt, zoom)) = app.primary.last_warped_from {
172 return Some(Transition::Replace(Warping::new_state(
173 ctx,
174 pt,
175 Some(zoom),
176 None,
177 &mut app.primary,
178 )));
179 }
180 return None;
181 }
182
183 let id = match (&line[1..line.len()]).parse::<usize>() {
184 Ok(idx) => match line.chars().next().unwrap() {
185 'r' => {
186 let r = app.primary.map.maybe_get_r(RoadID(idx))?;
187 ID::Lane(r.lanes[0].id)
188 }
189 'R' => {
190 let r = TransitRouteID(idx);
191 app.primary.map.maybe_get_tr(r)?;
192 return Some(Transition::Multi(vec![
193 Transition::Pop,
194 Transition::ModifyState(Box::new(move |state, ctx, app| {
195 if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
197 let mut actions = s.contextual_actions();
198 s.controls.common.as_mut().unwrap().launch_info_panel(
199 ctx,
200 app,
201 Tab::TransitRoute(r),
202 &mut actions,
203 );
204 }
205 })),
206 ]));
207 }
208 'l' => ID::Lane(LaneID::decode_u32(idx as u32)),
209 'L' => ID::ParkingLot(ParkingLotID(idx)),
210 'i' => ID::Intersection(IntersectionID(idx)),
211 'b' => ID::Building(BuildingID(idx)),
212 'a' => ID::Area(AreaID(idx)),
213 'p' => ID::Pedestrian(PedestrianID(idx)),
214 'P' => {
215 let id = PersonID(idx);
216 app.primary.sim.lookup_person(id)?;
217 return Some(Transition::Multi(vec![
218 Transition::Pop,
219 Transition::ModifyState(Box::new(move |state, ctx, app| {
220 if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
222 let mut actions = s.contextual_actions();
223 s.controls.common.as_mut().unwrap().launch_info_panel(
224 ctx,
225 app,
226 Tab::PersonTrips(id, BTreeMap::new()),
227 &mut actions,
228 );
229 }
230 })),
231 ]));
232 }
233 'c' => {
234 let c = app.primary.sim.lookup_car_id(idx)?;
236 ID::Car(c)
237 }
238 't' => {
239 let trip = TripID(idx);
240 let person = app.primary.sim.trip_to_person(trip)?;
241 return Some(Transition::Multi(vec![
242 Transition::Pop,
243 Transition::ModifyState(Box::new(move |state, ctx, app| {
244 if let Some(ref mut s) = state.downcast_mut::<SandboxMode>() {
246 let mut actions = s.contextual_actions();
247 s.controls.common.as_mut().unwrap().launch_info_panel(
248 ctx,
249 app,
250 Tab::PersonTrips(person, OpenTrip::single(trip)),
251 &mut actions,
252 );
253 }
254 })),
255 ]));
256 }
257 _ => {
258 return None;
259 }
260 },
261 Err(_) => {
262 return None;
263 }
264 };
265 if let Some(pt) = app.primary.canonical_point(id.clone()) {
266 println!("Warping to {:?}", id);
267 Some(Transition::Replace(Warping::new_state(
268 ctx,
269 pt,
270 Some(WARP_TO_CAM_ZOOM),
271 Some(id),
272 &mut app.primary,
273 )))
274 } else {
275 None
276 }
277}