From 64bde1e4a3c6791a7d6bce38b46931b3351a9314 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Tue, 25 Feb 2020 14:17:49 -0800 Subject: [PATCH] multiple hotkeys --- ezgui/src/event.rs | 31 +++++++++++++++++---------- ezgui/src/input.rs | 17 +++++++-------- ezgui/src/lib.rs | 2 +- ezgui/src/widgets/button.rs | 6 +++--- ezgui/src/widgets/modal_menu.rs | 6 +++--- ezgui/src/widgets/popup_menu.rs | 12 +++++------ ezgui/src/widgets/wizard.rs | 2 +- game/src/abtest/mod.rs | 2 +- game/src/common/mod.rs | 4 ++-- game/src/edit/mod.rs | 2 +- game/src/edit/traffic_signals.rs | 4 ++-- game/src/obj_actions.rs | 2 +- game/src/pregame.rs | 6 +++--- game/src/sandbox/gameplay/tutorial.rs | 10 ++++----- game/src/sandbox/mod.rs | 2 +- game/src/sandbox/speed.rs | 4 ++-- 16 files changed, 60 insertions(+), 52 deletions(-) diff --git a/ezgui/src/event.rs b/ezgui/src/event.rs index b90c613a73..db7008a9de 100644 --- a/ezgui/src/event.rs +++ b/ezgui/src/event.rs @@ -366,27 +366,36 @@ 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, +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum MultiKey { + Normal(Key), + LCtrl(Key), + Any(Vec), } impl MultiKey { - pub fn describe(self) -> String { - if self.lctrl { - format!("Ctrl+{}", self.key.describe()) - } else { - self.key.describe() + pub fn describe(&self) -> String { + match self { + MultiKey::Normal(key) => key.describe(), + MultiKey::LCtrl(key) => format!("Ctrl+{}", key.describe()), + MultiKey::Any(ref keys) => keys + .iter() + .map(|k| k.describe()) + .collect::>() + .join(", "), } } } // For easy ModalMenu construction pub fn hotkey(key: Key) -> Option { - Some(MultiKey { key, lctrl: false }) + Some(MultiKey::Normal(key)) } pub fn lctrl(key: Key) -> Option { - Some(MultiKey { key, lctrl: true }) + Some(MultiKey::LCtrl(key)) +} + +pub fn hotkeys(keys: Vec) -> Option { + Some(MultiKey::Any(keys)) } diff --git a/ezgui/src/input.rs b/ezgui/src/input.rs index 0c61c37c7e..b92c9a32f3 100644 --- a/ezgui/src/input.rs +++ b/ezgui/src/input.rs @@ -1,4 +1,4 @@ -use crate::{hotkey, lctrl, text, Canvas, Event, Key, Line, MultiKey, ScreenPt, Text}; +use crate::{text, Canvas, Event, Key, Line, MultiKey, ScreenPt, Text}; use geom::Duration; use std::collections::HashMap; @@ -55,7 +55,7 @@ impl UserInput { false } - pub fn new_was_pressed(&mut self, multikey: MultiKey) -> bool { + pub fn new_was_pressed(&mut self, multikey: &MultiKey) -> bool { // TODO Reserve? if self.event_consumed { @@ -63,13 +63,12 @@ impl UserInput { } if let Event::KeyPress(pressed) = self.event { - let mk = if self.lctrl_held { - lctrl(pressed) - } else { - hotkey(pressed) - } - .unwrap(); - if mk == multikey { + let same = match multikey { + MultiKey::Normal(key) => pressed == *key && !self.lctrl_held, + MultiKey::LCtrl(key) => pressed == *key && self.lctrl_held, + MultiKey::Any(ref keys) => !self.lctrl_held && keys.contains(&pressed), + }; + if same { self.consume_event(); return true; } diff --git a/ezgui/src/lib.rs b/ezgui/src/lib.rs index be536c6c18..a8f2267d0b 100644 --- a/ezgui/src/lib.rs +++ b/ezgui/src/lib.rs @@ -23,7 +23,7 @@ pub use crate::backend::Drawable; pub use crate::canvas::{Canvas, HorizontalAlignment, VerticalAlignment}; pub use crate::color::Color; pub use crate::drawing::{GeomBatch, GfxCtx, Prerender, RewriteColor}; -pub use crate::event::{hotkey, lctrl, Event, Key, MultiKey}; +pub use crate::event::{hotkey, hotkeys, lctrl, Event, Key, MultiKey}; pub use crate::event_ctx::EventCtx; pub use crate::input::UserInput; pub use crate::managed::{Composite, ManagedWidget, Outcome}; diff --git a/ezgui/src/widgets/button.rs b/ezgui/src/widgets/button.rs index 5d59f62f0b..a05c85f029 100644 --- a/ezgui/src/widgets/button.rs +++ b/ezgui/src/widgets/button.rs @@ -44,8 +44,7 @@ impl Button { draw_normal: ctx.upload(normal), draw_hovered: ctx.upload(hovered), - hotkey, - tooltip: if let Some(key) = hotkey { + tooltip: if let Some(ref key) = hotkey { let mut txt = Text::from(Line(key.describe()).fg(text::HOTKEY_COLOR).size(20)).with_bg(); txt.append(Line(format!(" - {}", tooltip))); @@ -53,6 +52,7 @@ impl Button { } else { Text::from(Line(tooltip).size(20)).with_bg() }, + hotkey, hitbox, hovering: false, @@ -89,7 +89,7 @@ impl Button { self.hovering = false; } - if let Some(hotkey) = self.hotkey { + if let Some(ref hotkey) = self.hotkey { if ctx.input.new_was_pressed(hotkey) { self.clicked = true; self.hovering = false; diff --git a/ezgui/src/widgets/modal_menu.rs b/ezgui/src/widgets/modal_menu.rs index 72f5193de0..7d593fac0f 100644 --- a/ezgui/src/widgets/modal_menu.rs +++ b/ezgui/src/widgets/modal_menu.rs @@ -113,7 +113,7 @@ impl ModalMenu { if !choice.active { continue; } - if let Some(hotkey) = choice.hotkey { + if let Some(ref hotkey) = choice.hotkey { if ctx.input.new_was_pressed(hotkey) { self.chosen_action = Some(choice.label.clone()); break; @@ -214,7 +214,7 @@ impl ModalMenu { for (idx, choice) in self.choices.iter().enumerate() { if choice.active { - if let Some(key) = choice.hotkey { + if let Some(ref key) = choice.hotkey { txt.add_appended(vec![ Line(key.describe()).fg(text::HOTKEY_COLOR), Line(format!(" - {}", choice.label)), @@ -228,7 +228,7 @@ impl ModalMenu { txt.highlight_last_line(text::SELECTED_COLOR); } } else { - if let Some(key) = choice.hotkey { + if let Some(ref key) = choice.hotkey { txt.add( Line(format!("{} - {}", key.describe(), choice.label)) .fg(text::INACTIVE_CHOICE_COLOR), diff --git a/ezgui/src/widgets/popup_menu.rs b/ezgui/src/widgets/popup_menu.rs index 06296a9e53..4febe56a9b 100644 --- a/ezgui/src/widgets/popup_menu.rs +++ b/ezgui/src/widgets/popup_menu.rs @@ -89,7 +89,7 @@ impl PopupMenu { if !choice.active { continue; } - if let Some(hotkey) = choice.hotkey { + if let Some(ref hotkey) = choice.hotkey { if ctx.input.new_was_pressed(hotkey) { self.state = InputResult::Done(choice.label.clone(), choice.data.clone()); return; @@ -98,7 +98,7 @@ impl PopupMenu { } // Handle nav keys - if ctx.input.new_was_pressed(hotkey(Key::Enter).unwrap()) { + if ctx.input.new_was_pressed(&hotkey(Key::Enter).unwrap()) { let choice = &self.choices[self.current_idx]; if choice.active { self.state = InputResult::Done(choice.label.clone(), choice.data.clone()); @@ -106,11 +106,11 @@ impl PopupMenu { } else { return; } - } else if ctx.input.new_was_pressed(hotkey(Key::UpArrow).unwrap()) { + } else if ctx.input.new_was_pressed(&hotkey(Key::UpArrow).unwrap()) { if self.current_idx > 0 { self.current_idx -= 1; } - } else if ctx.input.new_was_pressed(hotkey(Key::DownArrow).unwrap()) { + } else if ctx.input.new_was_pressed(&hotkey(Key::DownArrow).unwrap()) { if self.current_idx < self.choices.len() - 1 { self.current_idx += 1; } @@ -149,7 +149,7 @@ impl PopupMenu { for (idx, choice) in self.choices.iter().enumerate() { if choice.active { - if let Some(key) = choice.hotkey { + if let Some(ref key) = choice.hotkey { txt.add_appended(vec![ Line(key.describe()).fg(text::HOTKEY_COLOR), Line(format!(" - {}", choice.label)), @@ -158,7 +158,7 @@ impl PopupMenu { txt.add(Line(&choice.label)); } } else { - if let Some(key) = choice.hotkey { + if let Some(ref key) = choice.hotkey { txt.add( Line(format!("{} - {}", key.describe(), choice.label)) .fg(text::INACTIVE_CHOICE_COLOR), diff --git a/ezgui/src/widgets/wizard.rs b/ezgui/src/widgets/wizard.rs index 0bdc5a0a6e..456853c698 100644 --- a/ezgui/src/widgets/wizard.rs +++ b/ezgui/src/widgets/wizard.rs @@ -487,7 +487,7 @@ impl Choice { pub fn key(mut self, key: Key) -> Choice { assert_eq!(self.hotkey, None); - self.hotkey = Some(MultiKey { key, lctrl: false }); + self.hotkey = hotkey(key); self } diff --git a/game/src/abtest/mod.rs b/game/src/abtest/mod.rs index aa30820772..4c101832be 100644 --- a/game/src/abtest/mod.rs +++ b/game/src/abtest/mod.rs @@ -91,7 +91,7 @@ impl State for ABTestMode { ui.recalculate_current_selection(ctx); } - if ui.opts.dev && ctx.input.new_was_pressed(lctrl(Key::D).unwrap()) { + if ui.opts.dev && ctx.input.new_was_pressed(&lctrl(Key::D).unwrap()) { return Transition::Push(Box::new(DebugMode::new(ctx))); } diff --git a/game/src/common/mod.rs b/game/src/common/mod.rs index d41845c3a6..773b08cef7 100644 --- a/game/src/common/mod.rs +++ b/game/src/common/mod.rs @@ -48,10 +48,10 @@ impl CommonState { ui: &mut UI, maybe_speed: Option<&mut SpeedControls>, ) -> Option { - if ctx.input.new_was_pressed(lctrl(Key::S).unwrap()) { + if ctx.input.new_was_pressed(&lctrl(Key::S).unwrap()) { ui.opts.dev = !ui.opts.dev; } - if ui.opts.dev && ctx.input.new_was_pressed(lctrl(Key::J).unwrap()) { + if ui.opts.dev && ctx.input.new_was_pressed(&lctrl(Key::J).unwrap()) { return Some(Transition::Push(warp::EnteringWarp::new())); } diff --git a/game/src/edit/mod.rs b/game/src/edit/mod.rs index d932f2609c..4ecb28a212 100644 --- a/game/src/edit/mod.rs +++ b/game/src/edit/mod.rs @@ -110,7 +110,7 @@ impl State for EditMode { } } - if ui.opts.dev && ctx.input.new_was_pressed(lctrl(Key::D).unwrap()) { + if ui.opts.dev && ctx.input.new_was_pressed(&lctrl(Key::D).unwrap()) { return Transition::Push(Box::new(DebugMode::new(ctx))); } diff --git a/game/src/edit/traffic_signals.rs b/game/src/edit/traffic_signals.rs index 6d7182faa4..9193aa9f35 100644 --- a/game/src/edit/traffic_signals.rs +++ b/game/src/edit/traffic_signals.rs @@ -79,12 +79,12 @@ impl State for TrafficSignalEditor { ctx.canvas_movement(); // TODO Buttons for these... - if self.current_phase != 0 && ctx.input.new_was_pressed(hotkey(Key::UpArrow).unwrap()) { + if self.current_phase != 0 && ctx.input.new_was_pressed(&hotkey(Key::UpArrow).unwrap()) { self.change_phase(self.current_phase - 1, ui, ctx); } if self.current_phase != ui.primary.map.get_traffic_signal(self.i).phases.len() - 1 - && ctx.input.new_was_pressed(hotkey(Key::DownArrow).unwrap()) + && ctx.input.new_was_pressed(&hotkey(Key::DownArrow).unwrap()) { self.change_phase(self.current_phase + 1, ui, ctx); } diff --git a/game/src/obj_actions.rs b/game/src/obj_actions.rs index 8a5cf8a927..8ab0e007bb 100644 --- a/game/src/obj_actions.rs +++ b/game/src/obj_actions.rs @@ -30,7 +30,7 @@ impl PerObjectActions { if !(key == Key::I && lbl == "show info") { self.actions.borrow_mut().push((key, lbl)); } - !self.info_panel_open && ctx.input.new_was_pressed(hotkey(key).unwrap()) + !self.info_panel_open && ctx.input.new_was_pressed(&hotkey(key).unwrap()) } pub fn consume(&mut self) -> Vec<(Key, String)> { diff --git a/game/src/pregame.rs b/game/src/pregame.rs index 112850e140..ce3387b5ab 100644 --- a/game/src/pregame.rs +++ b/game/src/pregame.rs @@ -7,8 +7,8 @@ use crate::mission::MissionEditMode; use crate::sandbox::{GameplayMode, SandboxMode, TutorialPointer}; use crate::ui::UI; use ezgui::{ - hotkey, Button, Color, Composite, EventCtx, EventLoopMode, GfxCtx, JustDraw, Key, Line, - ManagedWidget, Text, + hotkey, hotkeys, Button, Color, Composite, EventCtx, EventLoopMode, GfxCtx, JustDraw, Key, + Line, ManagedWidget, Text, }; use geom::{Duration, Line, Pt2D, Speed}; use instant::Instant; @@ -37,7 +37,7 @@ impl TitleScreen { Text::from(Line("PLAY")), Color::BLUE, colors::HOVERING, - hotkey(Key::Space), + hotkeys(vec![Key::Space, Key::Enter]), "start game", ctx, )), diff --git a/game/src/sandbox/gameplay/tutorial.rs b/game/src/sandbox/gameplay/tutorial.rs index d4f513aa4e..4bfad99e08 100644 --- a/game/src/sandbox/gameplay/tutorial.rs +++ b/game/src/sandbox/gameplay/tutorial.rs @@ -12,8 +12,9 @@ use crate::sandbox::{ use crate::ui::UI; use abstutil::Timer; use ezgui::{ - hotkey, lctrl, Button, Color, Composite, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, - Line, ManagedWidget, Outcome, RewriteColor, ScreenPt, Text, VerticalAlignment, + hotkey, hotkeys, lctrl, Button, Color, Composite, EventCtx, GeomBatch, GfxCtx, + HorizontalAlignment, Key, Line, ManagedWidget, Outcome, RewriteColor, ScreenPt, Text, + VerticalAlignment, }; use geom::{Distance, Duration, PolyLine, Polygon, Pt2D, Statistic, Time}; use map_model::{BuildingID, IntersectionID, IntersectionType, LaneType, Map, RoadID}; @@ -1027,8 +1028,7 @@ impl TutorialState { ctx, "../data/system/assets/tools/next.svg", "next message", - // TODO Or space or enter - hotkey(Key::RightArrow), + hotkeys(vec![Key::RightArrow, Key::Space, Key::Enter]), ) } .margin(5), @@ -1038,7 +1038,7 @@ impl TutorialState { col.push(WrappedComposite::text_bg_button( ctx, "Try it", - hotkey(Key::RightArrow), + hotkeys(vec![Key::RightArrow, Key::Space, Key::Enter]), )); } diff --git a/game/src/sandbox/mod.rs b/game/src/sandbox/mod.rs index b77a3e7fd6..6c9d01b820 100644 --- a/game/src/sandbox/mod.rs +++ b/game/src/sandbox/mod.rs @@ -86,7 +86,7 @@ impl SandboxMode { } fn examine_objects(&self, ctx: &mut EventCtx, ui: &mut UI) -> Option { - if ui.opts.dev && ctx.input.new_was_pressed(lctrl(Key::D).unwrap()) { + if ui.opts.dev && ctx.input.new_was_pressed(&lctrl(Key::D).unwrap()) { return Some(Transition::Push(Box::new(DebugMode::new(ctx)))); } diff --git a/game/src/sandbox/speed.rs b/game/src/sandbox/speed.rs index abcc3f95d6..54ea1052ee 100644 --- a/game/src/sandbox/speed.rs +++ b/game/src/sandbox/speed.rs @@ -245,7 +245,7 @@ impl SpeedControls { None => {} } - if ctx.input.new_was_pressed(hotkey(Key::LeftArrow).unwrap()) { + if ctx.input.new_was_pressed(&hotkey(Key::LeftArrow).unwrap()) { match self.setting { SpeedSetting::Realtime => self.pause(ctx), SpeedSetting::Fast => { @@ -262,7 +262,7 @@ impl SpeedControls { } } } - if ctx.input.new_was_pressed(hotkey(Key::RightArrow).unwrap()) { + if ctx.input.new_was_pressed(&hotkey(Key::RightArrow).unwrap()) { match self.setting { SpeedSetting::Realtime => { if self.paused {