stop requiring hotkeys for TopMenu folders

This commit is contained in:
Dustin Carlino 2019-02-02 15:41:35 -08:00
parent 461efe4ef8
commit e0fd39bb7c
7 changed files with 70 additions and 71 deletions

View File

@ -6,7 +6,6 @@ touch `find * | grep '\.rs' | grep -v target | xargs`
# TODO Remove all of these exceptions
# TODO Report issues for some of these false positives
cargo clippy -- \
-A clippy::borrowed_box \
-A clippy::collapsible_if \
-A clippy::cyclomatic_complexity \
-A clippy::expect_fun_call \

View File

@ -9,9 +9,6 @@
- handle small roads again somehow?
- VERY overeager... ate half of the map
- can we capture snapshots of incremental changes?
- save initial map at every step, be able to load raw + initial with a focus point
- generic viewer should be easy... something that stores polygon and ID, wraps the quadtree, etc
- deal with loop roads
- manually draw a picture of the weird intersection to see what would look reasonable. i think we need original road bands from deleted stuff to make decent polygons.

View File

@ -25,7 +25,6 @@
## General ezgui stuff
- trigger screencap from a top menu debug thing WITHOUT a hotkey.
- optionally limit canvas scrolling/zooming to some map bounds
- T top menu doesnt know when we have a more urgent input thing going!
- cant use G for geom debug mode and contextual polygon debug

View File

@ -25,26 +25,27 @@ impl<S: UIState> GUI<RenderingHints> for UI<S> {
folders.push(Folder::new(
"File",
vec![
(Key::Comma, "show log console"),
(Key::L, "show legend"),
(Key::Escape, "quit"),
(Some(Key::Comma), "show log console"),
(Some(Key::L), "show legend"),
(Some(Key::Escape), "quit"),
],
));
if self.state.get_state().enable_debug_controls {
folders.push(Folder::new(
"Debug",
vec![
(Key::C, "find chokepoints"),
(Key::I, "validate map geometry"),
(Key::Num1, "show/hide buildings"),
(Key::Num2, "show/hide intersections"),
(Key::Num3, "show/hide lanes"),
(Key::Num4, "show/hide parcels"),
(Key::Num5, "show/hide areas"),
(Key::Num6, "show OSM colors"),
(Key::Num7, "show/hide extra shapes"),
(Key::Num9, "show/hide all turn icons"),
(Key::G, "show/hide geometry debug mode"),
(None, "screenshot everything"),
(None, "find chokepoints"),
(None, "validate map geometry"),
(Some(Key::Num1), "show/hide buildings"),
(Some(Key::Num2), "show/hide intersections"),
(Some(Key::Num3), "show/hide lanes"),
(Some(Key::Num4), "show/hide parcels"),
(Some(Key::Num5), "show/hide areas"),
(Some(Key::Num6), "show OSM colors"),
(Some(Key::Num7), "show/hide extra shapes"),
(Some(Key::Num9), "show/hide all turn icons"),
(None, "show/hide geometry debug mode"),
],
));
}
@ -52,38 +53,38 @@ impl<S: UIState> GUI<RenderingHints> for UI<S> {
Folder::new(
"Edit",
vec![
(Key::B, "manage A/B tests"),
(Key::Num8, "configure colors"),
(Key::N, "manage neighborhoods"),
(Key::Q, "manage map edits"),
(Key::E, "edit roads"),
(Key::W, "manage scenarios"),
(Some(Key::B), "manage A/B tests"),
(None, "configure colors"),
(Some(Key::N), "manage neighborhoods"),
(Some(Key::Q), "manage map edits"),
(Some(Key::E), "edit roads"),
(Some(Key::W), "manage scenarios"),
],
),
Folder::new(
"Simulation",
vec![
(Key::LeftBracket, "slow down sim"),
(Key::RightBracket, "speed up sim"),
(Key::O, "save sim state"),
(Key::Y, "load previous sim state"),
(Key::U, "load next sim state"),
(Key::Space, "run/pause sim"),
(Key::M, "run one step of sim"),
(Key::Dot, "show/hide sim info sidepanel"),
(Key::T, "start time traveling"),
(Key::D, "diff all A/B trips"),
(Key::S, "seed the sim with agents"),
(Key::LeftAlt, "swap the primary/secondary sim"),
(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), "run one step of sim"),
(Some(Key::Dot), "show/hide sim info sidepanel"),
(Some(Key::T), "start time traveling"),
(Some(Key::D), "diff all A/B trips"),
(Some(Key::S), "seed the sim with agents"),
(Some(Key::LeftAlt), "swap the primary/secondary sim"),
],
),
Folder::new(
"View",
vec![
(Key::Z, "show neighborhood summaries"),
(Key::Slash, "search for something"),
(Key::A, "show lanes with active traffic"),
(Key::J, "warp to an object"),
(Some(Key::Z), "show neighborhood summaries"),
(Some(Key::Slash), "search for something"),
(Some(Key::A), "show lanes with active traffic"),
(Some(Key::J), "warp to an object"),
],
),
]);
@ -218,10 +219,7 @@ impl<S: UIState> GUI<RenderingHints> for UI<S> {
ctx.input.populate_osd(&mut hints.osd);
// TODO a plugin should do this, even though it's such a tiny thing
if ctx
.input
.unimportant_key_pressed(Key::F1, "take screenshot")
{
if ctx.input.action_chosen("screenshot everything") {
let bounds = self.state.get_state().primary.map.get_bounds();
assert!(bounds.min_x == 0.0 && bounds.min_y == 0.0);
hints.mode = EventLoopMode::ScreenCaptureEverything {

View File

@ -215,9 +215,13 @@ impl UserInput {
}
if let Some(ref mut menu) = self.top_menu {
if let Some(key) = menu.actions.get(action).cloned() {
menu.valid_actions.insert(key);
self.unimportant_key_pressed(key, action)
if let Some(maybe_key) = menu.actions.get(action).cloned() {
menu.valid_actions.insert(action.to_string());
if let Some(key) = maybe_key {
self.unimportant_key_pressed(key, action)
} else {
false
}
} else {
panic!(
"action_chosen(\"{}\") doesn't match actions in the TopMenu!",
@ -291,11 +295,11 @@ impl UserInput {
// is semantically correct (think about holding down the key for deleting the current
// cycle), but causes annoying flickering.
if let Some(key) = self.modal_state.modes[mode].get_key(action) {
if self.modal_state.modes[mode].get_key(action).is_some() {
self.modal_state
.mut_active_mode(mode)
.unwrap()
.mark_active(key);
.mark_active(action);
// Don't check for the keypress here; Menu's event() will have already processed it
// and set chosen_action.
false

View File

@ -234,11 +234,11 @@ impl<T: Clone> Menu<T> {
}
// If there's no matching choice, be silent. The two callers don't care.
pub fn mark_active(&mut self, action_key: Key) {
for (key, _, ref mut active, _) in self.choices.iter_mut() {
if Some(action_key) == *key {
pub fn mark_active(&mut self, choice: &str) {
for (_, action, ref mut active, _) in self.choices.iter_mut() {
if choice == action {
if *active {
panic!("Menu choice with key {:?} was already active", action_key);
panic!("Menu choice for {} was already active", choice);
}
*active = true;
return;

View File

@ -9,29 +9,31 @@ const HORIZONTAL_PADDING_BETWEEN_ITEMS: f64 = 50.0;
pub struct TopMenu {
folders: Vec<Folder>,
pub(crate) actions: HashMap<String, Key>,
pub(crate) actions: HashMap<String, Option<Key>>,
highlighted: Option<usize>,
submenu: Option<(usize, Menu<Key>)>,
submenu: Option<(usize, Menu<()>)>,
// Reset every round
pub(crate) valid_actions: HashSet<Key>,
pub(crate) valid_actions: HashSet<String>,
}
impl TopMenu {
pub fn new(mut folders: Vec<Folder>, canvas: &Canvas) -> TopMenu {
let mut keys: HashSet<Key> = HashSet::new();
let mut actions: HashMap<String, Key> = HashMap::new();
let mut actions: HashMap<String, Option<Key>> = HashMap::new();
for f in &folders {
for (key, action) in &f.actions {
if keys.contains(key) {
panic!("TopMenu uses {:?} twice", key);
for (maybe_key, action) in &f.actions {
if let Some(key) = maybe_key {
if keys.contains(key) {
panic!("TopMenu uses {:?} twice", key);
}
keys.insert(*key);
}
keys.insert(*key);
if actions.contains_key(action) {
panic!("TopMenu assigns \"{:?}\" twice", action);
}
actions.insert(action.to_string(), *key);
actions.insert(action.to_string(), *maybe_key);
}
}
@ -59,7 +61,7 @@ impl TopMenu {
// Canceled means the top menu isn't blocking input, still active means it is, and done means
// something was clicked!
pub fn event(&mut self, input: &mut UserInput, canvas: &Canvas) -> InputResult<Key> {
pub fn event(&mut self, input: &mut UserInput, canvas: &Canvas) -> InputResult<()> {
if let Some(cursor) = input.get_moved_mouse() {
// TODO Could quickly filter out by checking y
self.highlighted = self
@ -81,7 +83,7 @@ impl TopMenu {
None,
f.actions
.iter()
.map(|(key, action)| (Some(*key), action.to_string(), *key))
.map(|(maybe_key, action)| (*maybe_key, action.to_string(), ()))
.collect(),
false,
Position::TopLeftAt(ScreenPt::new(f.rectangle.x1, f.rectangle.y2)),
@ -90,8 +92,8 @@ impl TopMenu {
menu.mark_all_inactive();
// valid_actions can't change once this submenu is created, so determine what
// actions are valid right now.
for key in &self.valid_actions {
menu.mark_active(*key);
for action in &self.valid_actions {
menu.mark_active(action);
}
self.submenu = Some((idx, menu));
return InputResult::StillActive;
@ -106,10 +108,10 @@ impl TopMenu {
self.submenu = None;
self.highlighted = None;
}
InputResult::Done(action, key) => {
InputResult::Done(action, _) => {
self.submenu = None;
self.highlighted = None;
return InputResult::Done(action, key);
return InputResult::Done(action, ());
}
};
}
@ -156,13 +158,13 @@ impl TopMenu {
pub struct Folder {
name: String,
actions: Vec<(Key, String)>,
actions: Vec<(Option<Key>, String)>,
rectangle: ScreenRectangle,
}
impl Folder {
pub fn new(name: &str, actions: Vec<(Key, &str)>) -> Folder {
pub fn new(name: &str, actions: Vec<(Option<Key>, &str)>) -> Folder {
Folder {
name: name.to_string(),
actions: actions