game/edit/traffic_signals/
picker.rs1use std::collections::BTreeSet;
2
3use crate::ID;
4use map_model::IntersectionID;
5use widgetry::{
6 hotkeys, Color, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel,
7 State, VerticalAlignment, Widget,
8};
9
10use crate::app::App;
11use crate::app::Transition;
12use crate::common::CommonState;
13use crate::edit::TrafficSignalEditor;
14use crate::sandbox::gameplay::GameplayMode;
15
16pub struct SignalPicker {
17 members: BTreeSet<IntersectionID>,
18 panel: Panel,
19 mode: GameplayMode,
20}
21
22impl SignalPicker {
23 pub fn new_state(
24 ctx: &mut EventCtx,
25 members: BTreeSet<IntersectionID>,
26 mode: GameplayMode,
27 ) -> Box<dyn State<App>> {
28 Box::new(SignalPicker {
29 panel: Panel::new_builder(Widget::col(vec![
30 Widget::row(vec![
31 Line("Select multiple traffic signals")
32 .small_heading()
33 .into_widget(ctx),
34 ctx.style().btn_close_widget(ctx),
35 ]),
36 make_btn(ctx, members.len()),
37 ]))
38 .aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
39 .build(ctx),
40 members,
41 mode,
42 })
43 }
44}
45
46impl State<App> for SignalPicker {
47 fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
48 ctx.canvas_movement();
49 if ctx.redo_mouseover() {
50 app.primary.current_selection =
51 app.mouseover_unzoomed_intersections(ctx).filter(|id| {
52 app.primary
53 .map
54 .maybe_get_traffic_signal(id.as_intersection())
55 .is_some()
56 });
57 }
58 if let Some(ID::Intersection(i)) = app.primary.current_selection {
59 if !self.members.contains(&i) && app.per_obj.left_click(ctx, "add this intersection") {
60 self.members.insert(i);
61 let btn = make_btn(ctx, self.members.len());
62 self.panel.replace(ctx, "edit", btn);
63 } else if self.members.contains(&i)
64 && app.per_obj.left_click(ctx, "remove this intersection")
65 {
66 self.members.remove(&i);
67 let btn = make_btn(ctx, self.members.len());
68 self.panel.replace(ctx, "edit", btn);
69 }
70 }
71
72 if let Outcome::Clicked(x) = self.panel.event(ctx) {
73 match x.as_ref() {
74 "close" => {
75 return Transition::Pop;
76 }
77 "edit" => {
78 return Transition::Replace(TrafficSignalEditor::new_state(
79 ctx,
80 app,
81 self.members.clone(),
82 self.mode.clone(),
83 ));
84 }
85 _ => unreachable!(),
86 }
87 }
88
89 Transition::Keep
90 }
91
92 fn draw(&self, g: &mut GfxCtx, app: &App) {
93 self.panel.draw(g);
94 CommonState::draw_osd(g, app);
95
96 let mut batch = GeomBatch::new();
97 for i in &self.members {
98 batch.push(
99 Color::RED.alpha(0.8),
100 app.primary.map.get_i(*i).polygon.clone(),
101 );
102 }
103 let draw = g.upload(batch);
104 g.redraw(&draw);
105 }
106}
107
108fn make_btn(ctx: &mut EventCtx, num: usize) -> Widget {
109 let title = match num {
110 0 => "Edit 0 signals".to_string(),
111 1 => "Edit 1 signal".to_string(),
112 _ => format!("Edit {} signals", num),
113 };
114 ctx.style()
115 .btn_solid_primary
116 .text(title)
117 .disabled(num == 0)
118 .hotkey(hotkeys(vec![Key::Enter, Key::E]))
119 .build_widget(ctx, "edit")
120}