From de929a5fb9c7e12580d28b162d749216f7a08dce Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sun, 14 Jul 2019 10:36:10 +0100 Subject: [PATCH] user-defined shortcuts --- .gitignore | 1 + editor/src/abtest/mod.rs | 1 + editor/src/common/mod.rs | 6 ++ editor/src/common/navigate.rs | 7 ++- editor/src/common/shortcuts.rs | 112 +++++++++++++++++++++++++++++++++ editor/src/common/warp.rs | 8 +-- editor/src/debug/mod.rs | 1 + editor/src/edit/mod.rs | 1 + editor/src/sandbox/mod.rs | 1 + ezgui/src/event.rs | 3 + ezgui/src/widgets/slider.rs | 4 +- ezgui/src/widgets/warper.rs | 7 ++- 12 files changed, 142 insertions(+), 10 deletions(-) create mode 100644 editor/src/common/shortcuts.rs diff --git a/.gitignore b/.gitignore index 6cd1263307..935307e1f2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ data/scenarios/* data/screenshots/*/*.png data/screenshots/*/combine.sh data/shapes/* +data/shortcuts/* diff --git a/editor/src/abtest/mod.rs b/editor/src/abtest/mod.rs index 0804190f29..a5d261cb25 100644 --- a/editor/src/abtest/mod.rs +++ b/editor/src/abtest/mod.rs @@ -53,6 +53,7 @@ impl ABTestMode { (hotkey(Key::Escape), "quit"), (hotkey(Key::J), "warp"), (hotkey(Key::K), "navigate"), + (hotkey(Key::SingleQuote), "shortcuts"), (hotkey(Key::F1), "take a screenshot"), ], ], diff --git a/editor/src/common/mod.rs b/editor/src/common/mod.rs index ae46895268..51aa8d126c 100644 --- a/editor/src/common/mod.rs +++ b/editor/src/common/mod.rs @@ -3,6 +3,7 @@ mod associated; mod navigate; mod route_explorer; mod route_viewer; +mod shortcuts; mod speed; mod time; mod trip_explorer; @@ -48,6 +49,11 @@ impl CommonState { if menu.action("navigate") { return Some(Transition::Push(Box::new(navigate::Navigator::new(ui)))); } + if menu.action("shortcuts") { + return Some(Transition::Push(Box::new( + shortcuts::ChoosingShortcut::new(ui), + ))); + } self.associated.event(ui); self.turn_cycler.event(ctx, ui); diff --git a/editor/src/common/navigate.rs b/editor/src/common/navigate.rs index 98140f4748..1f581ede8d 100644 --- a/editor/src/common/navigate.rs +++ b/editor/src/common/navigate.rs @@ -84,8 +84,9 @@ impl State for CrossStreet { warper: Warper::new( ctx, road.center_pts.dist_along(road.center_pts.length() / 2.0).0, + None, ), - id: ID::Lane(road.all_lanes()[0]), + id: Some(ID::Lane(road.all_lanes()[0])), }), EventLoopMode::Animation, ) @@ -104,8 +105,8 @@ impl State for CrossStreet { }; Transition::ReplaceWithMode( Box::new(Warping { - warper: Warper::new(ctx, pt), - id: ID::Lane(road.all_lanes()[0]), + warper: Warper::new(ctx, pt, None), + id: Some(ID::Lane(road.all_lanes()[0])), }), EventLoopMode::Animation, ) diff --git a/editor/src/common/shortcuts.rs b/editor/src/common/shortcuts.rs new file mode 100644 index 0000000000..8cd5285f51 --- /dev/null +++ b/editor/src/common/shortcuts.rs @@ -0,0 +1,112 @@ +use crate::common::warp::Warping; +use crate::game::{State, Transition}; +use crate::ui::UI; +use abstutil::Cloneable; +use ezgui::{ + hotkey, EventCtx, EventLoopMode, GfxCtx, Key, MultiKey, Warper, Wizard, WrappedWizard, +}; +use geom::Pt2D; +use serde_derive::{Deserialize, Serialize}; + +pub struct ChoosingShortcut { + wizard: Wizard, + shortcuts: Vec, +} + +impl ChoosingShortcut { + pub fn new(ui: &UI) -> ChoosingShortcut { + ChoosingShortcut { + wizard: Wizard::new(), + shortcuts: abstutil::load_all_objects::( + "shortcuts", + ui.primary.map.get_name(), + ) + .into_iter() + .map(|(_, s)| s) + .collect(), + } + } +} + +impl State for ChoosingShortcut { + fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition { + // TODO Ahh expensive + let mut shortcuts = vec![Shortcut { + name: "Create a new shortcut here".to_string(), + center: ctx.canvas.center_to_map_pt(), + cam_zoom: ctx.canvas.cam_zoom, + }]; + shortcuts.extend(self.shortcuts.clone()); + + if let Some(s) = choose_shortcut(&mut self.wizard.wrap(ctx), shortcuts, ui) { + return Transition::ReplaceWithMode( + Box::new(Warping { + warper: Warper::new(ctx, s.center, Some(s.cam_zoom)), + id: None, + }), + EventLoopMode::Animation, + ); + } else if self.wizard.aborted() { + return Transition::Pop; + } + Transition::Keep + } + + fn draw(&self, g: &mut GfxCtx, _: &UI) { + self.wizard.draw(g); + } +} + +fn choose_shortcut( + wizard: &mut WrappedWizard, + shortcuts: Vec, + ui: &UI, +) -> Option { + // TODO Handle >9 + // TODO Allow deleting + let keys = vec![ + Key::Num1, + Key::Num2, + Key::Num3, + Key::Num4, + Key::Num5, + Key::Num6, + Key::Num7, + Key::Num8, + Key::Num9, + ]; + + let choices: Vec<(Option, String, Shortcut)> = shortcuts + .into_iter() + .enumerate() + .map(|(idx, s)| { + if idx == 0 { + (None, s.name.clone(), s) + } else { + (hotkey(keys[idx - 1]), s.name.clone(), s) + } + }) + .collect(); + + let (_, mut s) = + wizard.choose_something("Jump to which shortcut?", Box::new(move || choices.clone()))?; + if s.name == "Create a new shortcut here" { + // TODO Enforce non-empty, unique names + let name = wizard.input_string("Name this shortcut")?; + s.name = name; + abstutil::save_json_object("shortcuts", ui.primary.map.get_name(), &s.name, &s); + wizard.abort(); + None + } else { + Some(s) + } +} + +#[derive(Clone, Serialize, Deserialize)] +struct Shortcut { + name: String, + center: Pt2D, + cam_zoom: f64, +} + +impl Cloneable for Shortcut {} diff --git a/editor/src/common/warp.rs b/editor/src/common/warp.rs index 30fa858aa9..256efca560 100644 --- a/editor/src/common/warp.rs +++ b/editor/src/common/warp.rs @@ -27,8 +27,8 @@ impl State for EnteringWarp { if let Some((id, pt)) = warp_point(to, &ui.primary) { Transition::ReplaceWithMode( Box::new(Warping { - warper: Warper::new(ctx, pt), - id, + warper: Warper::new(ctx, pt, None), + id: Some(id), }), EventLoopMode::Animation, ) @@ -47,7 +47,7 @@ impl State for EnteringWarp { pub struct Warping { pub warper: Warper, - pub id: ID, + pub id: Option, } impl State for Warping { @@ -55,7 +55,7 @@ impl State for Warping { if let Some(evmode) = self.warper.event(ctx) { Transition::KeepWithMode(evmode) } else { - ui.primary.current_selection = Some(self.id); + ui.primary.current_selection = self.id; Transition::Pop } } diff --git a/editor/src/debug/mod.rs b/editor/src/debug/mod.rs index a70c93a20e..e706cd6386 100644 --- a/editor/src/debug/mod.rs +++ b/editor/src/debug/mod.rs @@ -75,6 +75,7 @@ impl DebugMode { (lctrl(Key::E), "edit mode"), (hotkey(Key::J), "warp"), (hotkey(Key::K), "navigate"), + (hotkey(Key::SingleQuote), "shortcuts"), (hotkey(Key::F1), "take a screenshot"), ], ], diff --git a/editor/src/edit/mod.rs b/editor/src/edit/mod.rs index 3aedded429..6c02bca78b 100644 --- a/editor/src/edit/mod.rs +++ b/editor/src/edit/mod.rs @@ -43,6 +43,7 @@ impl EditMode { (lctrl(Key::D), "debug mode"), (hotkey(Key::J), "warp"), (hotkey(Key::K), "navigate"), + (hotkey(Key::SingleQuote), "shortcuts"), (hotkey(Key::F1), "take a screenshot"), ], ], diff --git a/editor/src/sandbox/mod.rs b/editor/src/sandbox/mod.rs index d0a700537a..6e42dcf245 100644 --- a/editor/src/sandbox/mod.rs +++ b/editor/src/sandbox/mod.rs @@ -63,6 +63,7 @@ impl SandboxMode { (lctrl(Key::E), "edit mode"), (hotkey(Key::J), "warp"), (hotkey(Key::K), "navigate"), + (hotkey(Key::SingleQuote), "shortcuts"), (hotkey(Key::F1), "take a screenshot"), ], ], diff --git a/ezgui/src/event.rs b/ezgui/src/event.rs index a68d2ffea4..7b5afa973b 100644 --- a/ezgui/src/event.rs +++ b/ezgui/src/event.rs @@ -133,6 +133,7 @@ pub enum Key { Semicolon, Colon, Equals, + SingleQuote, // Stuff without a straightforward single-character display Escape, Enter, @@ -209,6 +210,7 @@ impl Key { Key::Semicolon => Some(';'), Key::Colon => Some(':'), Key::Equals => Some(if shift_pressed { '+' } else { '=' }), + Key::SingleQuote => Some(if shift_pressed { '"' } else { '\'' }), Key::Escape | Key::Enter | Key::Tab @@ -318,6 +320,7 @@ impl Key { glutin::VirtualKeyCode::Semicolon => Key::Semicolon, glutin::VirtualKeyCode::Colon => Key::Colon, glutin::VirtualKeyCode::Equals => Key::Equals, + glutin::VirtualKeyCode::Apostrophe => Key::SingleQuote, glutin::VirtualKeyCode::Escape => Key::Escape, glutin::VirtualKeyCode::Return => Key::Enter, glutin::VirtualKeyCode::Tab => Key::Tab, diff --git a/ezgui/src/widgets/slider.rs b/ezgui/src/widgets/slider.rs index d1ce2a4134..4f574a4399 100644 --- a/ezgui/src/widgets/slider.rs +++ b/ezgui/src/widgets/slider.rs @@ -290,7 +290,7 @@ impl WarpingItemSlider { ctx: &mut EventCtx, ) -> WarpingItemSlider { WarpingItemSlider { - warper: Some(Warper::new(ctx, items[0].0)), + warper: Some(Warper::new(ctx, items[0].0, None)), slider: ItemSlider::new( items .into_iter() @@ -327,7 +327,7 @@ impl WarpingItemSlider { } let (_, (pt, _)) = self.slider.get(); - self.warper = Some(Warper::new(ctx, *pt)); + self.warper = Some(Warper::new(ctx, *pt, None)); // We just created a new warper, so... Some((EventLoopMode::Animation, done_warping)) } diff --git a/ezgui/src/widgets/warper.rs b/ezgui/src/widgets/warper.rs index 402dfe556a..5999bcde45 100644 --- a/ezgui/src/widgets/warper.rs +++ b/ezgui/src/widgets/warper.rs @@ -9,13 +9,16 @@ const MIN_ANIMATION_SPEED: f64 = 200.0; pub struct Warper { started: Instant, line: Option, + cam_zoom: (f64, f64), } impl Warper { - pub fn new(ctx: &EventCtx, pt: Pt2D) -> Warper { + pub fn new(ctx: &EventCtx, pt: Pt2D, target_cam_zoom: Option) -> Warper { + let z = ctx.canvas.cam_zoom; Warper { started: Instant::now(), line: Line::maybe_new(ctx.canvas.center_to_map_pt(), pt), + cam_zoom: (z, target_cam_zoom.unwrap_or(z)), } } @@ -36,9 +39,11 @@ impl Warper { let percent = abstutil::elapsed_seconds(self.started) / total_time; if percent >= 1.0 || ctx.input.nonblocking_is_keypress_event() { + ctx.canvas.cam_zoom = self.cam_zoom.1; ctx.canvas.center_on_map_pt(line.pt2()); None } else { + ctx.canvas.cam_zoom = self.cam_zoom.0 + percent * (self.cam_zoom.1 - self.cam_zoom.0); ctx.canvas .center_on_map_pt(line.dist_along(line.length() * percent)); Some(EventLoopMode::Animation)