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 121 122 123 124 125 126 127 128 129 130
use crate::app::{App, ShowEverything}; use crate::common::CommonState; use crate::edit::TrafficSignalEditor; use crate::game::{State, Transition}; use crate::helpers::ID; use crate::sandbox::gameplay::GameplayMode; use map_model::IntersectionID; use sim::DontDrawAgents; use std::collections::BTreeSet; use widgetry::{ hotkeys, Btn, Color, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, VerticalAlignment, Widget, }; 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> { Box::new(SignalPicker { panel: Panel::new(Widget::col(vec![ Widget::row(vec![ Line("Select multiple traffic signals") .small_heading() .draw(ctx), Btn::plaintext("X") .build(ctx, "close", Key::Escape) .align_right(), ]), make_btn(ctx, members.len()), ])) .aligned(HorizontalAlignment::Center, VerticalAlignment::Top) .build(ctx), members, mode, }) } } impl State 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.calculate_current_selection( ctx, &DontDrawAgents {}, &ShowEverything::new(), false, true, false, ); } if let Some(ID::Intersection(i)) = app.primary.current_selection { if app.primary.map.maybe_get_traffic_signal(i).is_some() { 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); } } else { app.primary.current_selection = None; } } else { app.primary.current_selection = None; } 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 { if num == 0 { return Btn::text_bg2("Edit 0 signals").inactive(ctx).named("edit"); } let title = if num == 1 { "Edit 1 signal".to_string() } else { format!("Edit {} signals", num) }; Btn::text_bg2(title).build(ctx, "edit", hotkeys(vec![Key::Enter, Key::E])) }