diff --git a/docs/lanes.md b/docs/lanes.md index cb0f261d02..42d54141ba 100644 --- a/docs/lanes.md +++ b/docs/lanes.md @@ -179,3 +179,15 @@ GUI refactoring thoughts: - Canvas has persistent state, GfxCtx is ephemeral every draw cycle - dont want to draw outside of render, but may want to readjust camera - compromise is maybe storing the last known window size in canvas, so we dont have to keep plumbing it between frames anyway. + + +One UI plugin at a time: +- What can plugins do? + - (rarely) contribute OSD lines (in some order) + - (rarely) do custom drawing (in some order) + - event handling + - mutate themselves or consume+return? + - indicate if the plugin was active and did stuff? +- just quit after handling each plugin? and do panning / some selection stuff earlier +- alright, atfer the current cleanup with short-circuiting... express as a more abstract monadish thing? or since there are side effects sometimes and inconsistent arguments and such, maybe not? + - consistently mutate a plugin or return a copy diff --git a/editor/src/animation.rs b/editor/src/animation.rs deleted file mode 100644 index cfc9fcc335..0000000000 --- a/editor/src/animation.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0 - -#[derive(PartialEq)] -pub enum EventLoopMode { - Animation, - InputOnly, -} - -impl EventLoopMode { - pub fn merge(self, other: EventLoopMode) -> EventLoopMode { - match self { - EventLoopMode::Animation => EventLoopMode::Animation, - _ => other, - } - } -} diff --git a/editor/src/experimental.rs b/editor/src/experimental.rs index a150fc95c2..1b8e1527a5 100644 --- a/editor/src/experimental.rs +++ b/editor/src/experimental.rs @@ -1,4 +1,3 @@ -use animation; use ezgui::GfxCtx; use ezgui::canvas::Canvas; use ezgui::input::UserInput; @@ -36,7 +35,7 @@ impl UI { } impl gui::GUI for UI { - fn event(mut self, input: &mut UserInput) -> (UI, animation::EventLoopMode) { + fn event(mut self, input: &mut UserInput) -> (UI, gui::EventLoopMode) { if input.unimportant_key_pressed(Key::Escape, "Press escape to quit") { process::exit(0); } @@ -59,7 +58,7 @@ impl gui::GUI for UI { self.canvas.handle_event(input.use_event_directly()); - (self, animation::EventLoopMode::InputOnly) + (self, gui::EventLoopMode::InputOnly) } // TODO Weird to mut self just to set window_size on the canvas diff --git a/editor/src/gui.rs b/editor/src/gui.rs index b73868c598..3473324810 100644 --- a/editor/src/gui.rs +++ b/editor/src/gui.rs @@ -1,14 +1,19 @@ -use animation; use ezgui::GfxCtx; use ezgui::input::UserInput; use piston::window::Size; use std; pub trait GUI { - fn event(self, input: &mut UserInput) -> (Self, animation::EventLoopMode) + fn event(self, input: &mut UserInput) -> (Self, EventLoopMode) where Self: std::marker::Sized; // TODO just take OSD stuff, not all of the input fn draw(&mut self, g: &mut GfxCtx, input: UserInput, window_size: Size); } + +#[derive(PartialEq)] +pub enum EventLoopMode { + Animation, + InputOnly, +} diff --git a/editor/src/main.rs b/editor/src/main.rs index 25ff9bf2c8..262624dd1b 100644 --- a/editor/src/main.rs +++ b/editor/src/main.rs @@ -34,7 +34,6 @@ use piston::input::RenderEvent; use piston::window::{Window, WindowSettings}; use structopt::StructOpt; -mod animation; mod colors; mod experimental; mod gui; @@ -107,7 +106,7 @@ fn run( mut glyphs: GlyphCache, mut gui: T, ) { - let mut last_event_mode = animation::EventLoopMode::InputOnly; + let mut last_event_mode = gui::EventLoopMode::InputOnly; while let Some(ev) = events.next(&mut window) { let mut input = UserInput::new(ev.clone()); @@ -115,7 +114,7 @@ fn run( gui = new_gui; // Don't constantly reset the events struct -- only when laziness changes. if new_event_mode != last_event_mode { - events.set_lazy(new_event_mode == animation::EventLoopMode::InputOnly); + events.set_lazy(new_event_mode == gui::EventLoopMode::InputOnly); last_event_mode = new_event_mode; } diff --git a/editor/src/plugins/classification.rs b/editor/src/plugins/classification.rs index add83a5480..faa1e5b34a 100644 --- a/editor/src/plugins/classification.rs +++ b/editor/src/plugins/classification.rs @@ -16,7 +16,7 @@ impl OsmClassifier { OsmClassifier { active: false } } - pub fn handle_event(&mut self, input: &mut UserInput) { + pub fn handle_event(&mut self, input: &mut UserInput) -> bool { let msg = if self.active { "Press 6 to stop showing OSM classes" } else { @@ -24,6 +24,9 @@ impl OsmClassifier { }; if input.unimportant_key_pressed(Key::D6, msg) { self.active = !self.active; + true + } else { + false } } diff --git a/editor/src/plugins/color_picker.rs b/editor/src/plugins/color_picker.rs index 9d5f9f3b58..d57e185537 100644 --- a/editor/src/plugins/color_picker.rs +++ b/editor/src/plugins/color_picker.rs @@ -29,29 +29,33 @@ impl ColorPicker { ColorPicker::Inactive } + // True if plugin was active pub fn handle_event( self, input: &mut UserInput, canvas: &Canvas, cs: &mut ColorScheme, - ) -> ColorPicker { + ) -> (ColorPicker, bool) { match self { ColorPicker::Inactive => { if input.unimportant_key_pressed(Key::D8, "Press 8 to configure colors") { - return ColorPicker::Choosing(menu::Menu::new( - Colors::iter().map(|c| c.to_string()).collect(), - )); + return ( + ColorPicker::Choosing(menu::Menu::new( + Colors::iter().map(|c| c.to_string()).collect(), + )), + true, + ); } - ColorPicker::Inactive + (ColorPicker::Inactive, false) } ColorPicker::Choosing(mut menu) => { // TODO arrow keys scroll canvas too match menu.event(input.use_event_directly()) { - menu::Result::Canceled => ColorPicker::Inactive, - menu::Result::StillActive => ColorPicker::Choosing(menu), + menu::Result::Canceled => (ColorPicker::Inactive, true), + menu::Result::StillActive => (ColorPicker::Choosing(menu), true), menu::Result::Done(choice) => { let c = Colors::from_str(&choice).unwrap(); - ColorPicker::PickingColor(c, cs.get(c)) + (ColorPicker::PickingColor(c, cs.get(c)), true) } } } @@ -64,7 +68,7 @@ impl ColorPicker { ), ) { cs.set(c, orig_color); - return ColorPicker::Inactive; + return (ColorPicker::Inactive, true); } if input.key_pressed( @@ -72,7 +76,7 @@ impl ColorPicker { &format!("Press enter to finalize new color for {:?}", c), ) { println!("Setting color for {:?}", c); - return ColorPicker::Inactive; + return (ColorPicker::Inactive, true); } if let Some(pos) = input.use_event_directly().mouse_cursor_args() { @@ -82,11 +86,11 @@ impl ColorPicker { let y = (pos[1] - (start_y as f64)) / (TILE_DIMS as f64) / 255.0; if x >= 0.0 && x <= 1.0 && y >= 0.0 && y <= 1.0 { cs.set(c, get_color(x as f32, y as f32)); - return ColorPicker::PickingColor(c, orig_color); + return (ColorPicker::PickingColor(c, orig_color), true); } } - ColorPicker::PickingColor(c, orig_color) + (ColorPicker::PickingColor(c, orig_color), true) } } } @@ -109,8 +113,8 @@ impl ColorPicker { g.draw_rectangle( color, [ - (x * TILE_DIMS + start_x) as f64, - (y * TILE_DIMS + start_y) as f64, + canvas.screen_to_map_x((x * TILE_DIMS + start_x) as f64), + canvas.screen_to_map_y((y * TILE_DIMS + start_y) as f64), TILE_DIMS as f64, TILE_DIMS as f64, ], diff --git a/editor/src/plugins/search.rs b/editor/src/plugins/search.rs index 4fe7ebe8c8..5583f7c7c7 100644 --- a/editor/src/plugins/search.rs +++ b/editor/src/plugins/search.rs @@ -34,22 +34,23 @@ impl SearchState { } // TODO Does this pattern where we consume self and return it work out nicer? - pub fn event(self, input: &mut UserInput) -> SearchState { + // True if active + pub fn event(self, input: &mut UserInput) -> (SearchState, bool) { match self { SearchState::Empty => { if input.unimportant_key_pressed(Key::Slash, "Press / to start searching") { - SearchState::EnteringSearch(TextBox::new()) + (SearchState::EnteringSearch(TextBox::new()), true) } else { - self + (self, false) } } SearchState::EnteringSearch(mut tb) => { if tb.event(input.use_event_directly()) { input.consume_event(); - SearchState::FilterOSM(tb.line) + (SearchState::FilterOSM(tb.line), true) } else { input.consume_event(); - SearchState::EnteringSearch(tb) + (SearchState::EnteringSearch(tb), true) } } SearchState::FilterOSM(filter) => { @@ -57,9 +58,9 @@ impl SearchState { Key::Return, &format!("Press enter to clear the current search for {}", filter), ) { - SearchState::Empty + (SearchState::Empty, true) } else { - SearchState::FilterOSM(filter) + (SearchState::FilterOSM(filter), true) } } } diff --git a/editor/src/plugins/selection.rs b/editor/src/plugins/selection.rs index 916aa37c69..e1f44cca3a 100644 --- a/editor/src/plugins/selection.rs +++ b/editor/src/plugins/selection.rs @@ -1,6 +1,5 @@ // Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0 -use animation; use colors::{ColorScheme, Colors}; use control::ControlMap; use ezgui::GfxCtx; @@ -26,6 +25,7 @@ pub enum ID { //Parcel(ParcelID), } +#[derive(Clone)] pub enum SelectionState { Empty, SelectedIntersection(IntersectionID), @@ -57,28 +57,15 @@ impl SelectionState { } // TODO consume self - pub fn event( - &self, - input: &mut UserInput, - map: &Map, - sim: &mut Sim, - ) -> (SelectionState, animation::EventLoopMode) { + pub fn event(&self, input: &mut UserInput, map: &Map) -> (SelectionState, bool) { // TODO simplify the way this is written match *self { - SelectionState::Empty => (SelectionState::Empty, animation::EventLoopMode::InputOnly), - SelectionState::SelectedIntersection(id) => ( - SelectionState::SelectedIntersection(id), - animation::EventLoopMode::InputOnly, - ), SelectionState::SelectedRoad(id, current_turn_index) => { if input.key_pressed( Key::LCtrl, &format!("Hold Ctrl to show road {:?}'s tooltip", id), ) { - ( - SelectionState::TooltipRoad(id), - animation::EventLoopMode::InputOnly, - ) + (SelectionState::TooltipRoad(id), true) } else if input .key_pressed(Key::Tab, "Press Tab to cycle through this road's turns") { @@ -86,58 +73,24 @@ impl SelectionState { Some(i) => i + 1, None => 0, }; - ( - SelectionState::SelectedRoad(id, Some(idx)), - animation::EventLoopMode::InputOnly, - ) + (SelectionState::SelectedRoad(id, Some(idx)), true) } else if input.key_pressed(Key::D, "press D to debug") { map.get_r(id).dump_debug(); - ( - SelectionState::SelectedRoad(id, current_turn_index), - animation::EventLoopMode::InputOnly, - ) + (SelectionState::SelectedRoad(id, current_turn_index), true) } else { - ( - SelectionState::SelectedRoad(id, current_turn_index), - animation::EventLoopMode::InputOnly, - ) + (self.clone(), false) } } SelectionState::TooltipRoad(id) => { if let Some(Button::Keyboard(Key::LCtrl)) = input.use_event_directly().release_args() { - ( - SelectionState::SelectedRoad(id, None), - animation::EventLoopMode::InputOnly, - ) + (SelectionState::SelectedRoad(id, None), true) } else { - ( - SelectionState::TooltipRoad(id), - animation::EventLoopMode::InputOnly, - ) + (self.clone(), false) } } - SelectionState::SelectedBuilding(id) => ( - SelectionState::SelectedBuilding(id), - animation::EventLoopMode::InputOnly, - ), - SelectionState::SelectedTurn(id) => ( - SelectionState::SelectedTurn(id), - animation::EventLoopMode::InputOnly, - ), - SelectionState::SelectedCar(id) => { - // TODO not sure if we should debug like this (pushing the bit down to all the - // layers representing an entity) or by using some scary global mutable singleton - if input.unimportant_key_pressed(Key::D, "press D to debug") { - sim.toggle_debug(id); - } - - ( - SelectionState::SelectedCar(id), - animation::EventLoopMode::InputOnly, - ) - } + _ => (self.clone(), false), } } @@ -248,10 +201,11 @@ impl Hider { } } - pub fn event(&mut self, input: &mut UserInput, state: &mut SelectionState) { + pub fn event(&mut self, input: &mut UserInput, state: &mut SelectionState) -> bool { if input.unimportant_key_pressed(Key::K, "Press k to unhide everything") { println!("Unhiding {} things", self.items.len()); self.items.clear(); + return true; } let item = match state { @@ -265,8 +219,10 @@ impl Hider { self.items.insert(id); println!("Hiding {:?}", id); *state = SelectionState::Empty; + return true; } } + false } pub fn show_r(&self, id: RoadID) -> bool { diff --git a/editor/src/plugins/steep.rs b/editor/src/plugins/steep.rs index 5f6005f28f..70a151a62d 100644 --- a/editor/src/plugins/steep.rs +++ b/editor/src/plugins/steep.rs @@ -33,7 +33,7 @@ impl SteepnessVisualizer { s } - pub fn handle_event(&mut self, input: &mut UserInput) { + pub fn handle_event(&mut self, input: &mut UserInput) -> bool { let msg = if self.active { "Press 5 to stop showing steepness" } else { @@ -41,6 +41,9 @@ impl SteepnessVisualizer { }; if input.unimportant_key_pressed(Key::D5, msg) { self.active = !self.active; + true + } else { + false } } diff --git a/editor/src/plugins/warp.rs b/editor/src/plugins/warp.rs index d32024c707..e5975c6acf 100644 --- a/editor/src/plugins/warp.rs +++ b/editor/src/plugins/warp.rs @@ -12,32 +12,33 @@ pub enum WarpState { } impl WarpState { + // True if active pub fn event( self, input: &mut UserInput, map: &Map, canvas: &mut Canvas, selection_state: &mut SelectionState, - ) -> WarpState { + ) -> (WarpState, bool) { match self { WarpState::Empty => { if input.unimportant_key_pressed( Key::J, "Press J to start searching for something to warp to", ) { - WarpState::EnteringSearch(TextBox::new()) + (WarpState::EnteringSearch(TextBox::new()), true) } else { - self + (self, false) } } WarpState::EnteringSearch(mut tb) => { if tb.event(input.use_event_directly()) { input.consume_event(); warp(tb.line, map, canvas, selection_state); - WarpState::Empty + (WarpState::Empty, true) } else { input.consume_event(); - WarpState::EnteringSearch(tb) + (WarpState::EnteringSearch(tb), true) } } } diff --git a/editor/src/ui.rs b/editor/src/ui.rs index cb7f678fab..f77561c7eb 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -3,7 +3,6 @@ // TODO this should just be a way to handle interactions between plugins use abstutil; -use animation; use colors::{ColorScheme, Colors}; use control::ControlMap; use control::{ModifiedStopSign, ModifiedTrafficSignal}; @@ -52,7 +51,10 @@ pub struct UI { show_icons: ToggleableLayer, debug_mode: ToggleableLayer, + // This is a particularly special plugin, since it's always kind of active and other things + // read/write it. current_selection_state: SelectionState, + hider: Hider, current_search_state: SearchState, warp: WarpState, @@ -327,12 +329,25 @@ impl UI { } impl gui::GUI for UI { - fn event(mut self, input: &mut UserInput) -> (UI, animation::EventLoopMode) { - let mut event_loop_mode = animation::EventLoopMode::InputOnly; - let mut edit_mode = false; + fn event(mut self, input: &mut UserInput) -> (UI, gui::EventLoopMode) { + // First update the camera and handle zoom + let old_zoom = self.canvas.cam_zoom; + self.canvas.handle_event(input.use_event_directly()); + let new_zoom = self.canvas.cam_zoom; + self.zoom_for_toggleable_layers(old_zoom, new_zoom); + + // Always handle mouseover + if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER { + self.current_selection_state = SelectionState::Empty; + } + if !self.canvas.is_dragging() && input.use_event_directly().mouse_cursor_args().is_some() + && new_zoom >= MIN_ZOOM_FOR_MOUSEOVER + { + let item = self.mouseover_something(); + self.current_selection_state = self.current_selection_state.handle_mouseover(item); + } if let Some(mut e) = self.traffic_signal_editor { - edit_mode = true; if e.event( input, &self.map, @@ -343,10 +358,10 @@ impl gui::GUI for UI { } else { self.traffic_signal_editor = Some(e); } + return (self, gui::EventLoopMode::InputOnly); } if let Some(mut e) = self.stop_sign_editor { - edit_mode = true; if e.event( input, &self.map, @@ -357,111 +372,86 @@ impl gui::GUI for UI { } else { self.stop_sign_editor = Some(e); } + return (self, gui::EventLoopMode::InputOnly); } - // TODO disabling temporarily since it conflicts with warp. need to solve the - // one-plugin-at-a-time problem. - if !edit_mode && false { - self.color_picker = + { + let (new_color_picker, active) = self.color_picker .handle_event(input, &mut self.canvas, &mut self.cs); - } - - self.current_search_state = self.current_search_state.event(input); - self.warp = self.warp.event( - input, - &self.map, - &mut self.canvas, - &mut self.current_selection_state, - ); - - if !edit_mode && self.sim_ctrl.event(input, &self.map, &self.control_map) { - event_loop_mode = event_loop_mode.merge(animation::EventLoopMode::Animation); - } - - let old_zoom = self.canvas.cam_zoom; - self.canvas.handle_event(input.use_event_directly()); - let new_zoom = self.canvas.cam_zoom; - self.zoom_for_toggleable_layers(old_zoom, new_zoom); - - if !edit_mode { - if self.show_roads.handle_event(input) { - if let SelectionState::SelectedRoad(_, _) = self.current_selection_state { - self.current_selection_state = SelectionState::Empty; - } - if let SelectionState::TooltipRoad(_) = self.current_selection_state { - self.current_selection_state = SelectionState::Empty; - } + self.color_picker = new_color_picker; + if active { + return (self, gui::EventLoopMode::InputOnly); } - if self.show_buildings.handle_event(input) { - if let SelectionState::SelectedBuilding(_) = self.current_selection_state { - self.current_selection_state = SelectionState::Empty; - } - } - if self.show_intersections.handle_event(input) { - if let SelectionState::SelectedIntersection(_) = self.current_selection_state { - self.current_selection_state = SelectionState::Empty; - } - } - self.show_parcels.handle_event(input); - self.show_icons.handle_event(input); - self.steepness_viz.handle_event(input); - self.osm_classifier.handle_event(input); - self.debug_mode.handle_event(input); } - if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER { - self.current_selection_state = SelectionState::Empty; - } - if !self.canvas.is_dragging() && input.use_event_directly().mouse_cursor_args().is_some() - && new_zoom >= MIN_ZOOM_FOR_MOUSEOVER { - let item = self.mouseover_something(); - self.current_selection_state = self.current_selection_state.handle_mouseover(item); + let (new_search, active) = self.current_search_state.event(input); + self.current_search_state = new_search; + if active { + return (self, gui::EventLoopMode::InputOnly); + } } - self.hider.event(input, &mut self.current_selection_state); - // TODO can't get this destructuring expressed right - let (new_selection_state, new_event_loop_mode) = - self.current_selection_state - .event(input, &self.map, &mut self.sim_ctrl.sim); - event_loop_mode = event_loop_mode.merge(new_event_loop_mode); - self.current_selection_state = new_selection_state; - match self.current_selection_state { - SelectionState::SelectedRoad(id, _) => { - if self.floodfiller.is_none() { - if input.key_pressed(Key::F, "Press F to start floodfilling from this road") { - self.floodfiller = Some(Floodfiller::new(id)); - } - } - if self.map.get_r(id).lane_type == map_model::LaneType::Driving { - if input.key_pressed(Key::A, "Press A to add a car starting from this road") { - if !self.sim_ctrl.sim.spawn_one_on_road(id) { - println!("No room, sorry"); - } - } - } + { + let (new_warp, active) = self.warp.event( + input, + &self.map, + &mut self.canvas, + &mut self.current_selection_state, + ); + self.warp = new_warp; + if active { + return (self, gui::EventLoopMode::InputOnly); } - SelectionState::SelectedIntersection(id) => { - if self.traffic_signal_editor.is_none() - && self.control_map.traffic_signals.contains_key(&id) - { - if input.key_pressed( - Key::E, - &format!("Press E to edit traffic signal for {:?}", id), - ) { - self.traffic_signal_editor = Some(TrafficSignalEditor::new(id)); - } - } - if self.stop_sign_editor.is_none() && self.control_map.stop_signs.contains_key(&id) - { - if input.key_pressed(Key::E, &format!("Press E to edit stop sign for {:?}", id)) - { - self.stop_sign_editor = Some(StopSignEditor::new(id)); - } - } + } + + if self.show_roads.handle_event(input) { + if let SelectionState::SelectedRoad(_, _) = self.current_selection_state { + self.current_selection_state = SelectionState::Empty; } - _ => {} + if let SelectionState::TooltipRoad(_) = self.current_selection_state { + self.current_selection_state = SelectionState::Empty; + } + return (self, gui::EventLoopMode::InputOnly); + } + + if self.show_buildings.handle_event(input) { + if let SelectionState::SelectedBuilding(_) = self.current_selection_state { + self.current_selection_state = SelectionState::Empty; + } + return (self, gui::EventLoopMode::InputOnly); + } + + if self.show_intersections.handle_event(input) { + if let SelectionState::SelectedIntersection(_) = self.current_selection_state { + self.current_selection_state = SelectionState::Empty; + } + return (self, gui::EventLoopMode::InputOnly); + } + + if self.show_parcels.handle_event(input) { + return (self, gui::EventLoopMode::InputOnly); + } + + if self.show_icons.handle_event(input) { + return (self, gui::EventLoopMode::InputOnly); + } + + if self.debug_mode.handle_event(input) { + return (self, gui::EventLoopMode::InputOnly); + } + + if self.steepness_viz.handle_event(input) { + return (self, gui::EventLoopMode::InputOnly); + } + + if self.osm_classifier.handle_event(input) { + return (self, gui::EventLoopMode::InputOnly); + } + + if self.hider.event(input, &mut self.current_selection_state) { + return (self, gui::EventLoopMode::InputOnly); } if let Some(mut f) = self.floodfiller { @@ -470,6 +460,7 @@ impl gui::GUI for UI { } else { self.floodfiller = Some(f); } + return (self, gui::EventLoopMode::InputOnly); } if let Some(mut v) = self.geom_validator { @@ -478,14 +469,70 @@ impl gui::GUI for UI { } else { self.geom_validator = Some(v); } + return (self, gui::EventLoopMode::InputOnly); + } + if input.unimportant_key_pressed(Key::I, "Validate map geometry") { + self.geom_validator = Some(Validator::new(&self.draw_map)); + return (self, gui::EventLoopMode::InputOnly); } if input.unimportant_key_pressed(Key::S, "Spawn 1000 cars in random places") { self.sim_ctrl.sim.spawn_many_on_empty_roads(&self.map, 1000); + return (self, gui::EventLoopMode::InputOnly); } - if input.unimportant_key_pressed(Key::I, "Validate map geometry") { - self.geom_validator = Some(Validator::new(&self.draw_map)); + match self.current_selection_state { + SelectionState::SelectedCar(id) => { + // TODO not sure if we should debug like this (pushing the bit down to all the + // layers representing an entity) or by using some scary global mutable singleton + if input.unimportant_key_pressed(Key::D, "press D to debug") { + self.sim_ctrl.sim.toggle_debug(id); + return (self, gui::EventLoopMode::InputOnly); + } + } + SelectionState::SelectedRoad(id, _) => { + if input.key_pressed(Key::F, "Press F to start floodfilling from this road") { + self.floodfiller = Some(Floodfiller::new(id)); + return (self, gui::EventLoopMode::InputOnly); + } + + if self.map.get_r(id).lane_type == map_model::LaneType::Driving { + if input.key_pressed(Key::A, "Press A to add a car starting from this road") { + if !self.sim_ctrl.sim.spawn_one_on_road(id) { + println!("No room, sorry"); + } + return (self, gui::EventLoopMode::InputOnly); + } + } + } + SelectionState::SelectedIntersection(id) => { + if self.control_map.traffic_signals.contains_key(&id) { + if input.key_pressed( + Key::E, + &format!("Press E to edit traffic signal for {:?}", id), + ) { + self.traffic_signal_editor = Some(TrafficSignalEditor::new(id)); + return (self, gui::EventLoopMode::InputOnly); + } + } + if self.control_map.stop_signs.contains_key(&id) { + if input.key_pressed(Key::E, &format!("Press E to edit stop sign for {:?}", id)) + { + self.stop_sign_editor = Some(StopSignEditor::new(id)); + return (self, gui::EventLoopMode::InputOnly); + } + } + } + _ => {} + } + + // Do this one lastish, since it conflicts with lots of other stuff + { + let (new_selection, active) = self.current_selection_state.event(input, &self.map); + self.current_selection_state = new_selection; + if active { + return (self, gui::EventLoopMode::InputOnly); + } } if input.unimportant_key_pressed(Key::Escape, "Press escape to quit") { @@ -503,7 +550,12 @@ impl gui::GUI for UI { process::exit(0); } - (self, event_loop_mode) + // Sim controller plugin is kind of always active? If nothing else ran, let it use keys. + if self.sim_ctrl.event(input, &self.map, &self.control_map) { + (self, gui::EventLoopMode::Animation) + } else { + (self, gui::EventLoopMode::InputOnly) + } } // TODO Weird to mut self just to set window_size on the canvas diff --git a/ezgui/src/canvas.rs b/ezgui/src/canvas.rs index 355e1151fe..98d78d92cb 100644 --- a/ezgui/src/canvas.rs +++ b/ezgui/src/canvas.rs @@ -3,13 +3,13 @@ use GfxCtx; use aabb_quadtree::geom::{Point, Rect}; use graphics::Transformed; -use piston::input::{Button, Event, Key, MouseButton, MouseCursorEvent, MouseScrollEvent, - PressEvent, ReleaseEvent}; +use piston::input::{Button, Event, MouseButton, MouseCursorEvent, MouseScrollEvent, PressEvent, + ReleaseEvent}; use piston::window::Size; use text; const ZOOM_SPEED: f64 = 0.05; -const PAN_SPEED: f64 = 10.0; +//const PAN_SPEED: f64 = 10.0; pub struct Canvas { pub cam_x: f64, @@ -60,7 +60,8 @@ impl Canvas { if let Some(Button::Mouse(MouseButton::Left)) = ev.release_args() { self.left_mouse_drag_from = None; } - if let Some(Button::Keyboard(key)) = ev.press_args() { + // These aren't used much now, and they conflict with other plugins + /*if let Some(Button::Keyboard(key)) = ev.press_args() { match key { Key::Up => self.cam_y -= PAN_SPEED, Key::Down => self.cam_y += PAN_SPEED, @@ -70,7 +71,7 @@ impl Canvas { Key::W => self.zoom_towards_mouse(ZOOM_SPEED), _ => {} } - } + }*/ if let Some(scroll) = ev.mouse_scroll_args() { self.zoom_towards_mouse(scroll[1] * ZOOM_SPEED); } @@ -123,10 +124,10 @@ impl Canvas { ) } - fn screen_to_map_x(&self, x: f64) -> f64 { + pub fn screen_to_map_x(&self, x: f64) -> f64 { (x + self.cam_x) / self.cam_zoom } - fn screen_to_map_y(&self, y: f64) -> f64 { + pub fn screen_to_map_y(&self, y: f64) -> f64 { (y + self.cam_y) / self.cam_zoom }