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