mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
user-defined shortcuts
This commit is contained in:
parent
faed0beac8
commit
de929a5fb9
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@ data/scenarios/*
|
||||
data/screenshots/*/*.png
|
||||
data/screenshots/*/combine.sh
|
||||
data/shapes/*
|
||||
data/shortcuts/*
|
||||
|
@ -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"),
|
||||
],
|
||||
],
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
)
|
||||
|
112
editor/src/common/shortcuts.rs
Normal file
112
editor/src/common/shortcuts.rs
Normal 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 {}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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"),
|
||||
],
|
||||
],
|
||||
|
@ -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"),
|
||||
],
|
||||
],
|
||||
|
@ -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"),
|
||||
],
|
||||
],
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user