game/edit/
routes.rs

1use geom::{Duration, Time};
2use map_model::{EditCmd, TransitRouteID};
3use widgetry::{
4    EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, Spinner, State, TextExt,
5    VerticalAlignment, Widget,
6};
7
8use crate::app::App;
9use crate::app::Transition;
10use crate::edit::apply_map_edits;
11
12pub struct RouteEditor {
13    panel: Panel,
14    route: TransitRouteID,
15}
16
17impl RouteEditor {
18    pub fn new_state(ctx: &mut EventCtx, app: &mut App, id: TransitRouteID) -> Box<dyn State<App>> {
19        app.primary.current_selection = None;
20
21        let route = app.primary.map.get_tr(id);
22        Box::new(RouteEditor {
23            panel: Panel::new_builder(Widget::col(vec![
24                Widget::row(vec![
25                    Line("Route editor").small_heading().into_widget(ctx),
26                    ctx.style().btn_close_widget(ctx),
27                ]),
28                Line(&route.long_name).into_widget(ctx),
29                // TODO This UI needs design, just something to start plumbing the edits
30                Widget::row(vec![
31                    "Frequency".text_widget(ctx),
32                    Spinner::widget(
33                        ctx,
34                        "freq_mins",
35                        (Duration::minutes(1), Duration::hours(2)),
36                        Duration::hours(1),
37                        Duration::minutes(1),
38                    ),
39                ]),
40                ctx.style()
41                    .btn_solid_primary
42                    .text("Apply")
43                    .hotkey(Key::Enter)
44                    .build_def(ctx),
45            ]))
46            .aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
47            .build(ctx),
48            route: id,
49        })
50    }
51}
52
53impl State<App> for RouteEditor {
54    fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
55        ctx.canvas_movement();
56
57        if let Outcome::Clicked(x) = self.panel.event(ctx) {
58            match x.as_ref() {
59                "close" => {
60                    return Transition::Pop;
61                }
62                "Apply" => {
63                    let freq = self.panel.spinner("freq_mins");
64                    let mut now = Time::START_OF_DAY;
65                    let mut hourly_times = Vec::new();
66                    while now <= Time::START_OF_DAY + Duration::hours(24) {
67                        hourly_times.push(now);
68                        now += freq;
69                    }
70
71                    let mut edits = app.primary.map.get_edits().clone();
72                    edits.commands.push(EditCmd::ChangeRouteSchedule {
73                        id: self.route,
74                        old: app.primary.map.get_tr(self.route).spawn_times.clone(),
75                        new: hourly_times,
76                    });
77                    apply_map_edits(ctx, app, edits);
78
79                    return Transition::Pop;
80                }
81                _ => unreachable!(),
82            }
83        }
84
85        Transition::Keep
86    }
87
88    fn draw(&self, g: &mut GfxCtx, _: &App) {
89        self.panel.draw(g);
90    }
91}