From 53e2c3e905005327bec4e1ade8c5008fb21992da Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Wed, 29 May 2019 13:28:51 -0700 Subject: [PATCH] hack in a way to easily check for ctrl+key. use to jump between sandbox, debug, and edit modes --- editor/src/abtest/mod.rs | 18 +++++------ editor/src/abtest/setup.rs | 6 ++-- editor/src/common/mod.rs | 12 ++++---- editor/src/debug/color_picker.rs | 6 ++-- editor/src/debug/mod.rs | 46 ++++++++++++++++++---------- editor/src/debug/polygons.rs | 14 ++++----- editor/src/edit/mod.rs | 24 ++++++++++++--- editor/src/edit/stop_signs.rs | 6 ++-- editor/src/edit/traffic_signals.rs | 24 +++++++-------- editor/src/game.rs | 18 ++++++----- editor/src/mission/all_trips.rs | 16 +++++----- editor/src/mission/dataviz.rs | 12 ++++---- editor/src/mission/individ_trips.rs | 4 +-- editor/src/mission/mod.rs | 16 +++++----- editor/src/mission/neighborhood.rs | 10 +++--- editor/src/mission/scenario.rs | 12 ++++---- editor/src/sandbox/mod.rs | 46 ++++++++++++++++++---------- editor/src/sandbox/route_explorer.rs | 4 +-- editor/src/sandbox/spawner.rs | 4 +-- editor/src/sandbox/time_travel.rs | 8 ++--- editor/src/tutorial.rs | 4 +-- ezgui/src/canvas.rs | 2 ++ ezgui/src/event.rs | 28 ++++++++++++++++- ezgui/src/input.rs | 11 +++++-- ezgui/src/lib.rs | 2 +- ezgui/src/widgets/menu.rs | 17 +++++++--- ezgui/src/widgets/modal_menu.rs | 10 ++++-- ezgui/src/widgets/slider.rs | 12 ++++---- ezgui/src/widgets/wizard.rs | 13 +++++--- 29 files changed, 248 insertions(+), 157 deletions(-) diff --git a/editor/src/abtest/mod.rs b/editor/src/abtest/mod.rs index c2170ec34b..addf1dc67e 100644 --- a/editor/src/abtest/mod.rs +++ b/editor/src/abtest/mod.rs @@ -5,7 +5,7 @@ use crate::game::{GameState, Mode}; use crate::render::DrawOptions; use crate::ui::{PerMapUI, ShowEverything, UI}; use abstutil::elapsed_seconds; -use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard}; +use ezgui::{hotkey, Color, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard}; use geom::{Duration, Line, PolyLine}; use map_model::LANE_THICKNESS; use sim::{Benchmark, TripID}; @@ -42,14 +42,14 @@ impl ABTestMode { "A/B Test Mode", vec![ vec![ - (Some(Key::Escape), "quit"), - (Some(Key::LeftBracket), "slow down sim"), - (Some(Key::RightBracket), "speed up sim"), - (Some(Key::Space), "run/pause sim"), - (Some(Key::M), "step forwards 0.1s"), - (Some(Key::S), "swap"), - (Some(Key::D), "diff all trips"), - (Some(Key::B), "stop diffing trips"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::LeftBracket), "slow down sim"), + (hotkey(Key::RightBracket), "speed up sim"), + (hotkey(Key::Space), "run/pause sim"), + (hotkey(Key::M), "step forwards 0.1s"), + (hotkey(Key::S), "swap"), + (hotkey(Key::D), "diff all trips"), + (hotkey(Key::B), "stop diffing trips"), ], CommonState::modal_menu_entries(), ] diff --git a/editor/src/abtest/setup.rs b/editor/src/abtest/setup.rs index 27d36e13f2..2983b0973a 100644 --- a/editor/src/abtest/setup.rs +++ b/editor/src/abtest/setup.rs @@ -1,7 +1,7 @@ use crate::abtest::{ABTestMode, State}; use crate::game::{GameState, Mode}; use crate::ui::{Flags, PerMapUI, UI}; -use ezgui::{EventCtx, GfxCtx, Key, LogScroller, ModalMenu, Wizard, WrappedWizard}; +use ezgui::{hotkey, EventCtx, GfxCtx, Key, LogScroller, ModalMenu, Wizard, WrappedWizard}; use map_model::Map; use sim::{ABTest, SimFlags}; use std::path::PathBuf; @@ -26,8 +26,8 @@ impl ABTestSetup { ModalMenu::new( &format!("A/B Test Editor for {}", ab_test.test_name), vec![ - (Some(Key::Escape), "quit"), - (Some(Key::R), "run A/B test"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::R), "run A/B test"), ], ctx, ), diff --git a/editor/src/common/mod.rs b/editor/src/common/mod.rs index 453998698a..42d4664ca8 100644 --- a/editor/src/common/mod.rs +++ b/editor/src/common/mod.rs @@ -8,8 +8,8 @@ use crate::render::DrawOptions; use crate::ui::UI; use abstutil::elapsed_seconds; use ezgui::{ - Color, EventCtx, EventLoopMode, GfxCtx, HorizontalAlignment, Key, ModalMenu, Text, - VerticalAlignment, + hotkey, Color, EventCtx, EventLoopMode, GfxCtx, HorizontalAlignment, Key, ModalMenu, MultiKey, + Text, VerticalAlignment, }; use geom::{Line, Pt2D}; use std::collections::BTreeSet; @@ -32,12 +32,12 @@ impl CommonState { } } - pub fn modal_menu_entries() -> Vec<(Option, &'static str)> { + pub fn modal_menu_entries() -> Vec<(Option, &'static str)> { vec![ - (Some(Key::J), "warp"), + (hotkey(Key::J), "warp"), // TODO This definitely conflicts with some modes. - (Some(Key::K), "navigate"), - (Some(Key::F1), "take a screenshot"), + (hotkey(Key::K), "navigate"), + (hotkey(Key::F1), "take a screenshot"), ] } diff --git a/editor/src/debug/color_picker.rs b/editor/src/debug/color_picker.rs index 9755167a04..be62fe8a6b 100644 --- a/editor/src/debug/color_picker.rs +++ b/editor/src/debug/color_picker.rs @@ -1,6 +1,6 @@ use crate::ui::UI; use ezgui::{ - Canvas, Color, EventCtx, GfxCtx, InputResult, Key, ModalMenu, ScreenPt, ScrollingMenu, + hotkey, Canvas, Color, EventCtx, GfxCtx, InputResult, Key, ModalMenu, ScreenPt, ScrollingMenu, }; use geom::{Distance, Polygon}; @@ -32,8 +32,8 @@ impl ColorPicker { ModalMenu::new( &format!("Color Picker for {}", name), vec![ - (Some(Key::Backspace), "revert"), - (Some(Key::Escape), "finalize"), + (hotkey(Key::Backspace), "revert"), + (hotkey(Key::Escape), "finalize"), ], ctx, ), diff --git a/editor/src/debug/mod.rs b/editor/src/debug/mod.rs index a8c67f385b..5921148342 100644 --- a/editor/src/debug/mod.rs +++ b/editor/src/debug/mod.rs @@ -6,16 +6,18 @@ mod objects; mod polygons; use crate::common::CommonState; +use crate::edit::EditMode; use crate::game::{GameState, Mode}; use crate::helpers::ID; use crate::render::DrawOptions; +use crate::sandbox::SandboxMode; use crate::ui::{ShowLayers, ShowObject, UI}; use abstutil::wraparound_get; use abstutil::Timer; use clipping::CPolygon; use ezgui::{ - Color, EventCtx, EventLoopMode, GfxCtx, InputResult, Key, ModalMenu, ScrollingMenu, Text, - TextBox, Wizard, + hotkey, lctrl, Color, EventCtx, EventLoopMode, GfxCtx, InputResult, Key, ModalMenu, + ScrollingMenu, Text, TextBox, Wizard, }; use geom::{Distance, PolyLine, Polygon, Pt2D}; use map_model::{IntersectionID, Map, RoadID}; @@ -69,22 +71,24 @@ impl DebugMode { "Debug Mode", vec![ vec![ - (Some(Key::Escape), "quit"), - (Some(Key::C), "show/hide chokepoints"), - (Some(Key::O), "clear original roads shown"), - (Some(Key::G), "clear intersection geometry"), - (Some(Key::H), "unhide everything"), - (Some(Key::Num1), "show/hide buildings"), - (Some(Key::Num2), "show/hide intersections"), - (Some(Key::Num3), "show/hide lanes"), - (Some(Key::Num4), "show/hide areas"), - (Some(Key::Num5), "show/hide extra shapes"), - (Some(Key::Num6), "show/hide geometry debug mode"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::C), "show/hide chokepoints"), + (hotkey(Key::O), "clear original roads shown"), + (hotkey(Key::G), "clear intersection geometry"), + (hotkey(Key::H), "unhide everything"), + (hotkey(Key::Num1), "show/hide buildings"), + (hotkey(Key::Num2), "show/hide intersections"), + (hotkey(Key::Num3), "show/hide lanes"), + (hotkey(Key::Num4), "show/hide areas"), + (hotkey(Key::Num5), "show/hide extra shapes"), + (hotkey(Key::Num6), "show/hide geometry debug mode"), (None, "screenshot everything"), - (Some(Key::Slash), "search OSM metadata"), - (Some(Key::M), "clear OSM search results"), - (Some(Key::S), "configure colors"), - (Some(Key::N), "show/hide neighborhood summaries"), + (hotkey(Key::Slash), "search OSM metadata"), + (hotkey(Key::M), "clear OSM search results"), + (hotkey(Key::S), "configure colors"), + (hotkey(Key::N), "show/hide neighborhood summaries"), + (lctrl(Key::S), "sandbox mode"), + (lctrl(Key::E), "edit mode"), ], CommonState::modal_menu_entries(), ] @@ -147,6 +151,14 @@ impl DebugMode { state.mode = Mode::SplashScreen(Wizard::new(), None); return EventLoopMode::InputOnly; } + if menu.action("sandbox mode") { + state.mode = Mode::Sandbox(SandboxMode::new(ctx)); + return EventLoopMode::InputOnly; + } + if menu.action("edit mode") { + state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui)); + return EventLoopMode::InputOnly; + } if menu.action("show/hide chokepoints") { if mode.chokepoints.is_some() { diff --git a/editor/src/debug/polygons.rs b/editor/src/debug/polygons.rs index a0f5073687..7efc6c461e 100644 --- a/editor/src/debug/polygons.rs +++ b/editor/src/debug/polygons.rs @@ -2,7 +2,7 @@ use crate::helpers::ID; use crate::render::calculate_corners; use crate::ui::UI; use abstutil::Timer; -use ezgui::{EventCtx, GfxCtx, ItemSlider, Key, Text}; +use ezgui::{hotkey, EventCtx, GfxCtx, ItemSlider, Key, Text}; use geom::{Polygon, Pt2D, Triangle}; pub struct PolygonDebugger { @@ -33,7 +33,7 @@ impl PolygonDebugger { pts.iter().map(|pt| Item::Point(*pt)).collect(), "Polygon Debugger", "point", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), center: Some(Pt2D::center(&pts_without_last)), @@ -54,7 +54,7 @@ impl PolygonDebugger { .collect(), "Polygon Debugger", "corner", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), center: None, @@ -75,7 +75,7 @@ impl PolygonDebugger { .collect(), "Polygon Debugger", "point", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), center: None, @@ -93,7 +93,7 @@ impl PolygonDebugger { .collect(), "Polygon Debugger", "triangle", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), center: None, @@ -115,7 +115,7 @@ impl PolygonDebugger { pts.iter().map(|pt| Item::Point(*pt)).collect(), "Polygon Debugger", "point", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), center: Some(center), @@ -133,7 +133,7 @@ impl PolygonDebugger { .collect(), "Polygon Debugger", "triangle", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), center: None, diff --git a/editor/src/edit/mod.rs b/editor/src/edit/mod.rs index cb0e46ae8b..92fba8b04e 100644 --- a/editor/src/edit/mod.rs +++ b/editor/src/edit/mod.rs @@ -2,15 +2,20 @@ mod stop_signs; mod traffic_signals; use crate::common::CommonState; +use crate::debug::DebugMode; use crate::game::{GameState, Mode}; use crate::helpers::ID; use crate::render::{ DrawCtx, DrawIntersection, DrawLane, DrawMap, DrawOptions, DrawTurn, Renderable, MIN_ZOOM_FOR_DETAIL, }; +use crate::sandbox::SandboxMode; use crate::ui::{ShowEverything, UI}; use abstutil::Timer; -use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard, WrappedWizard}; +use ezgui::{ + hotkey, lctrl, Color, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard, + WrappedWizard, +}; use map_model::{IntersectionID, Lane, LaneID, LaneType, Map, MapEdits, Road, TurnID, TurnType}; use std::collections::{BTreeSet, HashMap}; @@ -33,9 +38,11 @@ impl EditMode { "Map Edit Mode", vec![ vec![ - (Some(Key::Escape), "quit"), - (Some(Key::S), "save edits"), - (Some(Key::L), "load different edits"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::S), "save edits"), + (hotkey(Key::L), "load different edits"), + (lctrl(Key::S), "sandbox mode"), + (lctrl(Key::D), "debug mode"), ], CommonState::modal_menu_entries(), ] @@ -83,6 +90,15 @@ impl EditMode { state.mode = Mode::SplashScreen(Wizard::new(), None); return EventLoopMode::InputOnly; } + if menu.action("sandbox mode") { + state.mode = Mode::Sandbox(SandboxMode::new(ctx)); + return EventLoopMode::InputOnly; + } + if menu.action("debug mode") { + state.mode = Mode::Debug(DebugMode::new(ctx, &state.ui)); + return EventLoopMode::InputOnly; + } + // TODO Only if current edits are unsaved if menu.action("save edits") { state.mode = Mode::Edit(EditMode::Saving(Wizard::new())); diff --git a/editor/src/edit/stop_signs.rs b/editor/src/edit/stop_signs.rs index f11236136e..0bec196226 100644 --- a/editor/src/edit/stop_signs.rs +++ b/editor/src/edit/stop_signs.rs @@ -4,7 +4,7 @@ use crate::game::GameState; use crate::helpers::ID; use crate::render::{DrawIntersection, DrawOptions, DrawTurn}; use crate::ui::{ShowEverything, UI}; -use ezgui::{Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Text}; +use ezgui::{hotkey, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Text}; use geom::Polygon; use map_model::{IntersectionID, RoadID, TurnID, TurnPriority}; use std::collections::HashMap; @@ -37,8 +37,8 @@ impl StopSignEditor { menu: ModalMenu::new( "Stop Sign Editor", vec![ - (Some(Key::Escape), "quit"), - (Some(Key::R), "reset to default"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::R), "reset to default"), ], ctx, ), diff --git a/editor/src/edit/traffic_signals.rs b/editor/src/edit/traffic_signals.rs index d6c8be67be..89e2132882 100644 --- a/editor/src/edit/traffic_signals.rs +++ b/editor/src/edit/traffic_signals.rs @@ -5,7 +5,7 @@ use crate::helpers::ID; use crate::render::{draw_signal_cycle, draw_signal_diagram, DrawCtx, DrawOptions, DrawTurn}; use crate::ui::{ShowEverything, UI}; use abstutil::Timer; -use ezgui::{Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard}; +use ezgui::{hotkey, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard}; use geom::Duration; use map_model::{ControlTrafficSignal, Cycle, IntersectionID, Map, TurnID, TurnPriority, TurnType}; @@ -27,17 +27,17 @@ impl TrafficSignalEditor { let menu = ModalMenu::new( &format!("Traffic Signal Editor for {}", id), vec![ - (Some(Key::Escape), "quit"), - (Some(Key::D), "change cycle duration"), - (Some(Key::P), "choose a preset signal"), - (Some(Key::R), "reset to original"), - (Some(Key::K), "move current cycle up"), - (Some(Key::J), "move current cycle down"), - (Some(Key::UpArrow), "select previous cycle"), - (Some(Key::DownArrow), "select next cycle"), - (Some(Key::Backspace), "delete current cycle"), - (Some(Key::N), "add a new empty cycle"), - (Some(Key::M), "add a new pedestrian scramble cycle"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::D), "change cycle duration"), + (hotkey(Key::P), "choose a preset signal"), + (hotkey(Key::R), "reset to original"), + (hotkey(Key::K), "move current cycle up"), + (hotkey(Key::J), "move current cycle down"), + (hotkey(Key::UpArrow), "select previous cycle"), + (hotkey(Key::DownArrow), "select next cycle"), + (hotkey(Key::Backspace), "delete current cycle"), + (hotkey(Key::N), "add a new empty cycle"), + (hotkey(Key::M), "add a new pedestrian scramble cycle"), ], ctx, ); diff --git a/editor/src/game.rs b/editor/src/game.rs index b53e54583a..b593ca8e6a 100644 --- a/editor/src/game.rs +++ b/editor/src/game.rs @@ -8,7 +8,9 @@ use crate::sandbox::SandboxMode; use crate::tutorial::TutorialMode; use crate::ui::{EditorState, Flags, ShowEverything, UI}; use abstutil::elapsed_seconds; -use ezgui::{Canvas, EventCtx, EventLoopMode, GfxCtx, Key, LogScroller, UserInput, Wizard, GUI}; +use ezgui::{ + hotkey, Canvas, EventCtx, EventLoopMode, GfxCtx, Key, LogScroller, UserInput, Wizard, GUI, +}; use geom::{Duration, Line, Pt2D, Speed}; use map_model::Map; use rand::seq::SliceRandom; @@ -257,13 +259,13 @@ fn splash_screen( .choose_string_hotkeys( "Welcome to A/B Street!", vec![ - (Some(Key::S), sandbox), - (Some(Key::L), load_map), - (Some(Key::E), edit), - (Some(Key::T), tutorial), - (Some(Key::D), debug), - (Some(Key::M), mission), - (Some(Key::A), abtest), + (hotkey(Key::S), sandbox), + (hotkey(Key::L), load_map), + (hotkey(Key::E), edit), + (hotkey(Key::T), tutorial), + (hotkey(Key::D), debug), + (hotkey(Key::M), mission), + (hotkey(Key::A), abtest), (None, about), (None, quit), ], diff --git a/editor/src/mission/all_trips.rs b/editor/src/mission/all_trips.rs index cd85a00881..7df6557447 100644 --- a/editor/src/mission/all_trips.rs +++ b/editor/src/mission/all_trips.rs @@ -2,7 +2,7 @@ use crate::common::CommonState; use crate::mission::{clip_trips, Trip}; use crate::ui::{ShowEverything, UI}; use abstutil::prettyprint_usize; -use ezgui::{Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Slider, Text}; +use ezgui::{hotkey, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Slider, Text}; use geom::{Circle, Distance, Duration}; use map_model::LANE_THICKNESS; use popdat::psrc::Mode; @@ -52,13 +52,13 @@ impl TripsVisualizer { menu: ModalMenu::new( "Trips Visualizer", vec![ - (Some(Key::Escape), "quit"), - (Some(Key::Dot), "forwards 10 seconds"), - (Some(Key::RightArrow), "forwards 30 minutes"), - (Some(Key::Comma), "backwards 10 seconds"), - (Some(Key::LeftArrow), "backwards 30 minutes"), - (Some(Key::F), "goto start of day"), - (Some(Key::L), "goto end of day"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::Dot), "forwards 10 seconds"), + (hotkey(Key::RightArrow), "forwards 30 minutes"), + (hotkey(Key::Comma), "backwards 10 seconds"), + (hotkey(Key::LeftArrow), "backwards 30 minutes"), + (hotkey(Key::F), "goto start of day"), + (hotkey(Key::L), "goto end of day"), ], ctx, ), diff --git a/editor/src/mission/dataviz.rs b/editor/src/mission/dataviz.rs index 9ec801391d..c859a1538c 100644 --- a/editor/src/mission/dataviz.rs +++ b/editor/src/mission/dataviz.rs @@ -3,7 +3,7 @@ use crate::helpers::{rotating_color_total, ID}; use crate::ui::UI; use abstutil::{prettyprint_usize, Timer}; use ezgui::{ - Color, EventCtx, GfxCtx, HorizontalAlignment, Key, ModalMenu, Text, VerticalAlignment, + hotkey, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, ModalMenu, Text, VerticalAlignment, }; use geom::{Distance, Polygon, Pt2D}; use popdat::{Estimate, PopDat}; @@ -40,11 +40,11 @@ impl DataVisualizer { menu: ModalMenu::new( "Data Visualizer", vec![ - (Some(Key::Escape), "quit"), - (Some(Key::Space), "toggle table/bar chart"), - (Some(Key::Num1), "household vehicles"), - (Some(Key::Num2), "commute times"), - (Some(Key::Num3), "commute modes"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::Space), "toggle table/bar chart"), + (hotkey(Key::Num1), "household vehicles"), + (hotkey(Key::Num2), "commute times"), + (hotkey(Key::Num3), "commute modes"), ], ctx, ), diff --git a/editor/src/mission/individ_trips.rs b/editor/src/mission/individ_trips.rs index ca5d8dfc5e..7ed8d9e147 100644 --- a/editor/src/mission/individ_trips.rs +++ b/editor/src/mission/individ_trips.rs @@ -2,7 +2,7 @@ use crate::common::CommonState; use crate::mission::{clip_trips, Trip}; use crate::ui::{ShowEverything, UI}; use abstutil::{prettyprint_usize, Timer}; -use ezgui::{Color, EventCtx, GfxCtx, ItemSlider, Key, Text}; +use ezgui::{hotkey, Color, EventCtx, GfxCtx, ItemSlider, Key, Text}; use geom::{Circle, Distance, Speed}; use popdat::PopDat; @@ -22,7 +22,7 @@ impl TripsVisualizer { trips, "Trips Visualizer", "trip", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), } diff --git a/editor/src/mission/mod.rs b/editor/src/mission/mod.rs index eae70a48fb..04329fdd1b 100644 --- a/editor/src/mission/mod.rs +++ b/editor/src/mission/mod.rs @@ -10,7 +10,7 @@ use crate::sandbox::SandboxMode; use crate::ui::ShowEverything; use crate::ui::UI; use abstutil::{skip_fail, Timer}; -use ezgui::{EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Wizard}; +use ezgui::{hotkey, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Wizard}; use geom::{Distance, Duration, PolyLine}; use map_model::{BuildingID, Map, PathRequest, Position}; use sim::SidewalkSpot; @@ -38,13 +38,13 @@ impl MissionEditMode { state: State::Exploring(ModalMenu::new( "Mission Edit Mode", vec![ - (Some(Key::Escape), "quit"), - (Some(Key::D), "visualize population data"), - (Some(Key::T), "visualize individual PSRC trips"), - (Some(Key::A), "visualize all PSRC trips"), - (Some(Key::S), "set up simulation with PSRC trips"), - (Some(Key::N), "manage neighborhoods"), - (Some(Key::W), "manage scenarios"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::D), "visualize population data"), + (hotkey(Key::T), "visualize individual PSRC trips"), + (hotkey(Key::A), "visualize all PSRC trips"), + (hotkey(Key::S), "set up simulation with PSRC trips"), + (hotkey(Key::N), "manage neighborhoods"), + (hotkey(Key::W), "manage scenarios"), ], ctx, )), diff --git a/editor/src/mission/neighborhood.rs b/editor/src/mission/neighborhood.rs index 44ef9b4327..2b464b1889 100644 --- a/editor/src/mission/neighborhood.rs +++ b/editor/src/mission/neighborhood.rs @@ -1,5 +1,5 @@ use crate::ui::UI; -use ezgui::{Color, EventCtx, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard}; +use ezgui::{hotkey, Color, EventCtx, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard}; use geom::{Circle, Distance, Line, Polygon, Pt2D}; use map_model::{Map, NeighborhoodBuilder}; @@ -18,10 +18,10 @@ impl NeighborhoodEditor { ModalMenu::new( &format!("Neighborhood Editor for {}", name), vec![ - (Some(Key::Escape), "quit"), - (Some(Key::S), "save"), - (Some(Key::X), "export as an Osmosis polygon filter"), - (Some(Key::P), "add a new point"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::S), "save"), + (hotkey(Key::X), "export as an Osmosis polygon filter"), + (hotkey(Key::P), "add a new point"), ], ctx, ) diff --git a/editor/src/mission/scenario.rs b/editor/src/mission/scenario.rs index 65d759a517..02def84a77 100644 --- a/editor/src/mission/scenario.rs +++ b/editor/src/mission/scenario.rs @@ -5,7 +5,7 @@ use crate::sandbox::SandboxMode; use crate::ui::UI; use abstutil::WeightedUsizeChoice; use ezgui::{ - Color, Drawable, EventCtx, GfxCtx, Key, LogScroller, ModalMenu, Wizard, WrappedWizard, + hotkey, Color, Drawable, EventCtx, GfxCtx, Key, LogScroller, ModalMenu, Wizard, WrappedWizard, }; use geom::{Distance, Duration, PolyLine, Pt2D}; use map_model::{IntersectionID, Map, Neighborhood}; @@ -24,11 +24,11 @@ impl ScenarioEditor { ModalMenu::new( &format!("Scenario Editor for {}", name), vec![ - (Some(Key::Escape), "quit"), - (Some(Key::S), "save"), - (Some(Key::E), "edit"), - (Some(Key::I), "instantiate"), - (Some(Key::V), "visualize"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::S), "save"), + (hotkey(Key::E), "edit"), + (hotkey(Key::I), "instantiate"), + (hotkey(Key::V), "visualize"), ], ctx, ) diff --git a/editor/src/sandbox/mod.rs b/editor/src/sandbox/mod.rs index 7490c817b7..e606308b66 100644 --- a/editor/src/sandbox/mod.rs +++ b/editor/src/sandbox/mod.rs @@ -5,11 +5,13 @@ mod spawner; mod time_travel; use crate::common::CommonState; +use crate::debug::DebugMode; +use crate::edit::EditMode; use crate::game::{GameState, Mode}; use crate::render::DrawOptions; use crate::ui::ShowEverything; use abstutil::elapsed_seconds; -use ezgui::{EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard}; +use ezgui::{hotkey, lctrl, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard}; use geom::Duration; use sim::{Benchmark, Sim, TripID}; use std::time::Instant; @@ -54,24 +56,26 @@ impl SandboxMode { "Sandbox Mode", vec![ vec![ - (Some(Key::Escape), "quit"), - (Some(Key::LeftBracket), "slow down sim"), - (Some(Key::RightBracket), "speed up sim"), - (Some(Key::O), "save sim state"), - (Some(Key::Y), "load previous sim state"), - (Some(Key::U), "load next sim state"), - (Some(Key::Space), "run/pause sim"), - (Some(Key::M), "step forwards 0.1s"), - (Some(Key::N), "step forwards 10 mins"), - (Some(Key::X), "reset sim"), - (Some(Key::S), "seed the sim with agents"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::LeftBracket), "slow down sim"), + (hotkey(Key::RightBracket), "speed up sim"), + (hotkey(Key::O), "save sim state"), + (hotkey(Key::Y), "load previous sim state"), + (hotkey(Key::U), "load next sim state"), + (hotkey(Key::Space), "run/pause sim"), + (hotkey(Key::M), "step forwards 0.1s"), + (hotkey(Key::N), "step forwards 10 mins"), + (hotkey(Key::X), "reset sim"), + (hotkey(Key::S), "seed the sim with agents"), // TODO Strange to always have this. Really it's a case of stacked modal? - (Some(Key::F), "stop following agent"), - (Some(Key::R), "stop showing agent's route"), + (hotkey(Key::F), "stop following agent"), + (hotkey(Key::R), "stop showing agent's route"), // TODO This should probably be a debug thing instead - (Some(Key::L), "show/hide route for all agents"), - (Some(Key::A), "show/hide active traffic"), - (Some(Key::T), "start time traveling"), + (hotkey(Key::L), "show/hide route for all agents"), + (hotkey(Key::A), "show/hide active traffic"), + (hotkey(Key::T), "start time traveling"), + (lctrl(Key::D), "debug mode"), + (lctrl(Key::E), "edit mode"), ], CommonState::modal_menu_entries(), ] @@ -206,6 +210,14 @@ impl SandboxMode { state.mode = Mode::SplashScreen(Wizard::new(), None); return EventLoopMode::InputOnly; } + if mode.menu.action("debug mode") { + state.mode = Mode::Debug(DebugMode::new(ctx, &state.ui)); + return EventLoopMode::InputOnly; + } + if mode.menu.action("edit mode") { + state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui)); + return EventLoopMode::InputOnly; + } if mode.menu.action("slow down sim") { mode.desired_speed -= ADJUST_SPEED; diff --git a/editor/src/sandbox/route_explorer.rs b/editor/src/sandbox/route_explorer.rs index 1a7e0307bc..dc0c0f0769 100644 --- a/editor/src/sandbox/route_explorer.rs +++ b/editor/src/sandbox/route_explorer.rs @@ -2,7 +2,7 @@ use crate::common::Warper; use crate::helpers::ID; use crate::render::DrawTurn; use crate::ui::UI; -use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, ItemSlider, Key, Text}; +use ezgui::{hotkey, Color, EventCtx, EventLoopMode, GfxCtx, ItemSlider, Key, Text}; use geom::{Distance, Polygon}; use map_model::{Traversable, LANE_THICKNESS}; use sim::AgentID; @@ -63,7 +63,7 @@ impl RouteExplorer { steps, "Route Explorer", "step", - vec![(Some(Key::Escape), "quit")], + vec![(hotkey(Key::Escape), "quit")], ctx, ), entire_trace, diff --git a/editor/src/sandbox/spawner.rs b/editor/src/sandbox/spawner.rs index a10aeb5fd1..3e5bcb2dae 100644 --- a/editor/src/sandbox/spawner.rs +++ b/editor/src/sandbox/spawner.rs @@ -3,7 +3,7 @@ use crate::helpers::ID; use crate::render::DrawOptions; use crate::ui::{ShowEverything, UI}; use abstutil::Timer; -use ezgui::{EventCtx, GfxCtx, Key, ModalMenu}; +use ezgui::{hotkey, EventCtx, GfxCtx, Key, ModalMenu}; use geom::{Duration, PolyLine}; use map_model::{ BuildingID, IntersectionID, IntersectionType, LaneType, PathRequest, Position, LANE_THICKNESS, @@ -38,7 +38,7 @@ impl AgentSpawner { ui: &mut UI, sandbox_menu: &mut ModalMenu, ) -> Option { - let menu = ModalMenu::new("Agent Spawner", vec![(Some(Key::Escape), "quit")], ctx); + let menu = ModalMenu::new("Agent Spawner", vec![(hotkey(Key::Escape), "quit")], ctx); let map = &ui.primary.map; match ui.primary.current_selection { Some(ID::Building(id)) => { diff --git a/editor/src/sandbox/time_travel.rs b/editor/src/sandbox/time_travel.rs index c51a29afb5..07fb8c0e5c 100644 --- a/editor/src/sandbox/time_travel.rs +++ b/editor/src/sandbox/time_travel.rs @@ -1,6 +1,6 @@ use crate::ui::UI; use abstutil::MultiMap; -use ezgui::{EventCtx, GfxCtx, Key, ModalMenu, Text}; +use ezgui::{hotkey, EventCtx, GfxCtx, Key, ModalMenu, Text}; use geom::Duration; use map_model::{Map, Traversable}; use sim::{CarID, DrawCarInput, DrawPedestrianInput, GetDrawAgents, PedestrianID}; @@ -30,9 +30,9 @@ impl TimeTravel { menu: ModalMenu::new( "Time Traveler", vec![ - (Some(Key::Escape), "quit"), - (Some(Key::Comma), "rewind"), - (Some(Key::Dot), "forwards"), + (hotkey(Key::Escape), "quit"), + (hotkey(Key::Comma), "rewind"), + (hotkey(Key::Dot), "forwards"), ], ctx, ), diff --git a/editor/src/tutorial.rs b/editor/src/tutorial.rs index c7eb5c249d..94eb82141c 100644 --- a/editor/src/tutorial.rs +++ b/editor/src/tutorial.rs @@ -1,7 +1,7 @@ use crate::game::{GameState, Mode}; use crate::render::DrawOptions; use crate::ui::{ShowEverything, UI}; -use ezgui::{EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard}; +use ezgui::{hotkey, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard}; use geom::Pt2D; pub struct TutorialMode { @@ -21,7 +21,7 @@ impl TutorialMode { ui.primary.reset_sim(); TutorialMode { - menu: ModalMenu::new("Tutorial", vec![(Some(Key::Escape), "quit")], ctx), + menu: ModalMenu::new("Tutorial", vec![(hotkey(Key::Escape), "quit")], ctx), state: State::Part1(ctx.canvas.center_to_map_pt()), } } diff --git a/ezgui/src/canvas.rs b/ezgui/src/canvas.rs index 5444487c56..71c8fa4218 100644 --- a/ezgui/src/canvas.rs +++ b/ezgui/src/canvas.rs @@ -33,6 +33,7 @@ pub struct Canvas { // Kind of just ezgui state awkwardly stuck here... pub(crate) hide_modal_menus: bool, + pub(crate) lctrl_held: bool, } impl Canvas { @@ -59,6 +60,7 @@ impl Canvas { covered_areas: RefCell::new(Vec::new()), hide_modal_menus: false, + lctrl_held: false, } } diff --git a/ezgui/src/event.rs b/ezgui/src/event.rs index 858d6598d3..a68d2ffea4 100644 --- a/ezgui/src/event.rs +++ b/ezgui/src/event.rs @@ -237,7 +237,7 @@ impl Key { } } - pub fn describe(self: Key) -> String { + pub fn describe(self) -> String { match self { Key::Escape => "Escape".to_string(), Key::Enter => "Enter".to_string(), @@ -350,3 +350,29 @@ impl Key { }) } } + +// TODO This is not an ideal representation at all. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct MultiKey { + pub key: Key, + pub lctrl: bool, +} + +impl MultiKey { + pub fn describe(self) -> String { + if self.lctrl { + format!("Ctrl+{}", self.key.describe()) + } else { + self.key.describe() + } + } +} + +// For easy ModalMenu construction +pub fn hotkey(key: Key) -> Option { + Some(MultiKey { key, lctrl: false }) +} + +pub fn lctrl(key: Key) -> Option { + Some(MultiKey { key, lctrl: true }) +} diff --git a/ezgui/src/input.rs b/ezgui/src/input.rs index 312bf805d4..4d9a758454 100644 --- a/ezgui/src/input.rs +++ b/ezgui/src/input.rs @@ -1,5 +1,5 @@ use crate::widgets::{Menu, Position}; -use crate::{text, Canvas, Event, InputResult, Key, ScreenPt, Text}; +use crate::{hotkey, text, Canvas, Event, InputResult, Key, ScreenPt, Text}; use std::collections::{BTreeMap, BTreeSet, HashMap}; // As we check for user input, record the input and the thing that would happen. This will let us @@ -41,7 +41,7 @@ impl ContextMenu { Text::new(), actions .into_iter() - .map(|(hotkey, action)| (Some(hotkey), action, hotkey)) + .map(|(key, action)| (hotkey(key), action, key)) .collect(), false, false, @@ -71,6 +71,13 @@ impl UserInput { canvas.window_height = height; } + if input.event == Event::KeyPress(Key::LeftControl) { + canvas.lctrl_held = true; + } + if input.event == Event::KeyRelease(Key::LeftControl) { + canvas.lctrl_held = false; + } + // Create the context menu here, even if one already existed. if input.right_mouse_button_pressed() { assert!(!input.event_consumed); diff --git a/ezgui/src/lib.rs b/ezgui/src/lib.rs index 895d652612..05d0543b89 100644 --- a/ezgui/src/lib.rs +++ b/ezgui/src/lib.rs @@ -12,7 +12,7 @@ mod widgets; pub use crate::canvas::{Canvas, HorizontalAlignment, VerticalAlignment, BOTTOM_LEFT, CENTERED}; pub use crate::color::Color; pub use crate::drawing::{GeomBatch, GfxCtx}; -pub use crate::event::{Event, Key}; +pub use crate::event::{hotkey, lctrl, Event, Key, MultiKey}; pub use crate::event_ctx::{Drawable, EventCtx, Prerender}; pub use crate::input::UserInput; pub use crate::runner::{run, EventLoopMode, GUI}; diff --git a/ezgui/src/widgets/menu.rs b/ezgui/src/widgets/menu.rs index 0e21f1bd24..7c099f4e48 100644 --- a/ezgui/src/widgets/menu.rs +++ b/ezgui/src/widgets/menu.rs @@ -1,12 +1,14 @@ use crate::screen_geom::ScreenRectangle; -use crate::{text, Canvas, Event, GfxCtx, InputResult, Key, ScreenPt, Text}; +use crate::{ + hotkey, lctrl, text, Canvas, Event, GfxCtx, InputResult, Key, MultiKey, ScreenPt, Text, +}; use std::collections::HashSet; // Stores some associated data with each choice pub struct Menu { prompt: Text, // The bool is whether this choice is active or not - choices: Vec<(Option, String, bool, T)>, + choices: Vec<(Option, String, bool, T)>, current_idx: Option, mouse_in_bounds: bool, keys_enabled: bool, @@ -35,7 +37,7 @@ impl Position { &self, canvas: &Canvas, prompt: Text, - choices: &Vec<(Option, String, bool, T)>, + choices: &Vec<(Option, String, bool, T)>, ) -> Geometry { // This is actually a constant, effectively... let row_height = canvas.line_height(text::FONT_SIZE); @@ -99,7 +101,7 @@ impl Position { impl Menu { pub fn new( prompt: Text, - raw_choices: Vec<(Option, String, T)>, + raw_choices: Vec<(Option, String, T)>, keys_enabled: bool, hideable: bool, pos: Position, @@ -220,8 +222,13 @@ impl Menu { } if let Event::KeyPress(key) = ev { + let pressed = if canvas.lctrl_held { + lctrl(key) + } else { + hotkey(key) + }; for (maybe_key, choice, active, data) in &self.choices { - if *active && Some(key) == *maybe_key { + if *active && pressed == *maybe_key { return InputResult::Done(choice.to_string(), data.clone()); } } diff --git a/ezgui/src/widgets/modal_menu.rs b/ezgui/src/widgets/modal_menu.rs index 16eb5584a2..c33876685e 100644 --- a/ezgui/src/widgets/modal_menu.rs +++ b/ezgui/src/widgets/modal_menu.rs @@ -1,5 +1,5 @@ use crate::widgets::{Menu, Position}; -use crate::{EventCtx, GfxCtx, InputResult, Key, Text}; +use crate::{EventCtx, GfxCtx, InputResult, MultiKey, Text}; pub struct ModalMenu { menu: Menu<()>, @@ -7,12 +7,16 @@ pub struct ModalMenu { } impl ModalMenu { - pub fn new(prompt_line: &str, choices: Vec<(Option, &str)>, ctx: &EventCtx) -> ModalMenu { + pub fn new( + prompt_line: &str, + choices: Vec<(Option, &str)>, + ctx: &EventCtx, + ) -> ModalMenu { let mut menu = Menu::new( Text::prompt(prompt_line), choices .into_iter() - .map(|(key, action)| (key, action.to_string(), ())) + .map(|(multikey, action)| (multikey, action.to_string(), ())) .collect(), false, true, diff --git a/ezgui/src/widgets/slider.rs b/ezgui/src/widgets/slider.rs index 6a1fcd0500..b33f53044c 100644 --- a/ezgui/src/widgets/slider.rs +++ b/ezgui/src/widgets/slider.rs @@ -1,5 +1,5 @@ use crate::screen_geom::ScreenRectangle; -use crate::{Color, EventCtx, GfxCtx, Key, ModalMenu, Text}; +use crate::{hotkey, Color, EventCtx, GfxCtx, Key, ModalMenu, MultiKey, Text}; use geom::{Distance, Polygon, Pt2D}; // Pixels @@ -169,7 +169,7 @@ impl ItemSlider { items: Vec, menu_title: &str, noun: &str, - other_choices: Vec<(Option, &str)>, + other_choices: Vec<(Option, &str)>, ctx: &mut EventCtx, ) -> ItemSlider { // Lifetime funniness... @@ -180,10 +180,10 @@ impl ItemSlider { let first = format!("first {}", noun); let last = format!("last {}", noun); choices.extend(vec![ - (Some(Key::LeftArrow), prev.as_str()), - (Some(Key::RightArrow), next.as_str()), - (Some(Key::Comma), first.as_str()), - (Some(Key::Dot), last.as_str()), + (hotkey(Key::LeftArrow), prev.as_str()), + (hotkey(Key::RightArrow), next.as_str()), + (hotkey(Key::Comma), first.as_str()), + (hotkey(Key::Dot), last.as_str()), ]); ItemSlider { diff --git a/ezgui/src/widgets/wizard.rs b/ezgui/src/widgets/wizard.rs index 4281ae96c4..ba81ac03ae 100644 --- a/ezgui/src/widgets/wizard.rs +++ b/ezgui/src/widgets/wizard.rs @@ -1,5 +1,7 @@ use crate::widgets::{Menu, Position}; -use crate::{Canvas, GfxCtx, InputResult, Key, LogScroller, Text, TextBox, UserInput}; +use crate::{ + hotkey, Canvas, GfxCtx, InputResult, Key, LogScroller, MultiKey, Text, TextBox, UserInput, +}; use abstutil::Cloneable; use std::collections::VecDeque; @@ -207,9 +209,9 @@ impl<'a> WrappedWizard<'a> { )); return None; } - let boxed_choices: Vec<(Option, String, Box)> = choices + let boxed_choices: Vec<(Option, String, Box)> = choices .into_iter() - .map(|(key, s, item)| (key, s, item.clone_box())) + .map(|(key, s, item)| (key.and_then(hotkey), s, item.clone_box())) .collect(); self.wizard.menu = Some(Menu::new( Text::prompt(query), @@ -275,13 +277,14 @@ impl<'a> WrappedWizard<'a> { pub fn choose_string_hotkeys( &mut self, query: &str, - choices: Vec<(Option, &str)>, + choices: Vec<(Option, &str)>, ) -> Option { // Clone the choices outside of the closure to get around the fact that choices_generator's // lifetime isn't correctly specified. let copied_choices: Vec<(Option, String, ())> = choices .into_iter() - .map(|(key, s)| (key, s.to_string(), ())) + // TODO Argh, change all the APIs to take MultiKey + .map(|(multikey, s)| (multikey.map(|mk| mk.key), s.to_string(), ())) .collect(); self.choose_something(query, Box::new(move || copied_choices.clone())) .map(|(s, _)| s)