user-defined shortcuts

This commit is contained in:
Dustin Carlino 2019-07-14 10:36:10 +01:00
parent faed0beac8
commit de929a5fb9
12 changed files with 142 additions and 10 deletions

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ data/scenarios/*
data/screenshots/*/*.png
data/screenshots/*/combine.sh
data/shapes/*
data/shortcuts/*

View File

@ -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"),
],
],

View File

@ -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);

View File

@ -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,
)

View File

@ -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<Shortcut>,
}
impl ChoosingShortcut {
pub fn new(ui: &UI) -> ChoosingShortcut {
ChoosingShortcut {
wizard: Wizard::new(),
shortcuts: abstutil::load_all_objects::<Shortcut>(
"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<Shortcut>,
ui: &UI,
) -> Option<Shortcut> {
// 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<MultiKey>, 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 {}

View File

@ -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<ID>,
}
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
}
}

View File

@ -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"),
],
],

View File

@ -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"),
],
],

View File

@ -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"),
],
],

View File

@ -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,

View File

@ -290,7 +290,7 @@ impl<T> WarpingItemSlider<T> {
ctx: &mut EventCtx,
) -> WarpingItemSlider<T> {
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<T> WarpingItemSlider<T> {
}
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))
}

View File

@ -9,13 +9,16 @@ const MIN_ANIMATION_SPEED: f64 = 200.0;
pub struct Warper {
started: Instant,
line: Option<Line>,
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<f64>) -> 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)