diff options
author | Joe Wilm <joe@jwilm.com> | 2016-12-26 19:00:27 -0500 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2016-12-26 19:00:27 -0500 |
commit | d28a7344731c4cd913687a893334555feed4e270 (patch) | |
tree | f7848fc477d6a7a561769e57fa7441cced08f558 /src/input.rs | |
parent | 358c9fa17d9b088bae79f7d352a8cc21878f6303 (diff) | |
download | alacritty-d28a7344731c4cd913687a893334555feed4e270.tar.gz alacritty-d28a7344731c4cd913687a893334555feed4e270.zip |
Refactor bindings and fix binding tests
The somewhat redundant KeyBinding and MouseBinding types were removed in
favor of a Binding<T> type. This allows all binding code to be reused
for both scenarios.
The binding tests were fixed by only asserting on `is_triggered_by()`
instead of checking that the action escape sequence was delivered
properly.
Diffstat (limited to 'src/input.rs')
-rw-r--r-- | src/input.rs | 153 |
1 files changed, 53 insertions, 100 deletions
diff --git a/src/input.rs b/src/input.rs index 7e499e78..92379be9 100644 --- a/src/input.rs +++ b/src/input.rs @@ -59,7 +59,7 @@ pub struct ActionContext<'a, N: 'a> { /// /// This is the shared component of `MouseBinding` and `KeyBinding` #[derive(Debug, Clone)] -pub struct Binding { +pub struct Binding<T> { /// Modifier keys required to activate binding pub mods: Mods, @@ -71,57 +71,57 @@ pub struct Binding { /// excluded terminal modes where the binding won't be activated pub notmode: TermMode, -} -#[derive(Debug, Clone)] -pub struct KeyBinding { - pub key: VirtualKeyCode, - pub binding: Binding, + /// This property is used as part of the trigger detection code. + /// + /// For example, this might be a key like "G", or a mouse button. + pub trigger: T, } -#[derive(Debug, Clone)] -pub struct MouseBinding { - pub button: MouseButton, - pub binding: Binding, -} +/// Bindings that are triggered by a keyboard key +pub type KeyBinding = Binding<VirtualKeyCode>; + +/// Bindings that are triggered by a mouse button +pub type MouseBinding = Binding<MouseButton>; -impl KeyBinding { +impl<T: Eq> Binding<T> { #[inline] fn is_triggered_by( &self, mode: &TermMode, mods: &Mods, - key: &VirtualKeyCode + input: &T ) -> bool { - // Check key first since bindings are stored in one big list. This is + // Check input first since bindings are stored in one big list. This is // the most likely item to fail so prioritizing it here allows more // checks to be short circuited. - self.key == *key && self.binding.is_triggered_by(mode, mods) + self.trigger == *input && + self.mode_matches(mode) && + self.not_mode_matches(mode) && + self.mods_match(mods) } +} +impl<T> Binding<T> { + /// Execute the action associate with this binding #[inline] fn execute<'a, N: Notify>(&self, ctx: &mut ActionContext<'a, N>) { - self.binding.action.execute(ctx) + self.action.execute(ctx) } -} -impl MouseBinding { #[inline] - fn is_triggered_by( - &self, - mode: &TermMode, - mods: &Mods, - button: &MouseButton - ) -> bool { - // Check key first since bindings are stored in one big list. This is - // the most likely item to fail so prioritizing it here allows more - // checks to be short circuited. - self.button == *button && self.binding.is_triggered_by(mode, mods) + fn mode_matches(&self, mode: &TermMode) -> bool { + self.mode.is_empty() || mode.intersects(self.mode) } #[inline] - fn execute<'a, N: Notify>(&self, ctx: &mut ActionContext<'a, N>) { - self.binding.action.execute(ctx) + fn not_mode_matches(&self, mode: &TermMode) -> bool { + self.notmode.is_empty() || !mode.intersects(self.notmode) + } + + #[inline] + fn mods_match(&self, mods: &Mods) -> bool { + self.mods.is_all() || *mods == self.mods } } @@ -178,36 +178,6 @@ impl From<&'static str> for Action { } } -impl Binding { - /// Check if this binding is triggered by the current terminal mode, - /// modifier keys, and key pressed. - #[inline] - pub fn is_triggered_by( - &self, - mode: &TermMode, - mods: &Mods, - ) -> bool { - self.mode_matches(mode) && - self.not_mode_matches(mode) && - self.mods_match(mods) - } - - #[inline] - fn mode_matches(&self, mode: &TermMode) -> bool { - self.mode.is_empty() || mode.intersects(self.mode) - } - - #[inline] - fn not_mode_matches(&self, mode: &TermMode) -> bool { - self.notmode.is_empty() || !mode.intersects(self.notmode) - } - - #[inline] - fn mods_match(&self, mods: &Mods) -> bool { - self.mods.is_all() || *mods == self.mods - } -} - impl<'a, N: Notify + 'a> Processor<'a, N> { #[inline] pub fn mouse_moved(&mut self, x: u32, y: u32) { @@ -401,25 +371,11 @@ impl<'a, N: Notify + 'a> Processor<'a, N> { #[cfg(test)] mod tests { - use std::borrow::Cow; - use glutin::{mods, VirtualKeyCode}; use term::mode; - use super::{Action, Processor, Binding, KeyBinding}; - - /// Receiver that keeps a copy of any strings it is notified with - #[derive(Default)] - struct Receiver { - pub got: Option<String> - } - - impl super::Notify for Receiver { - fn notify<B: Into<Cow<'static, [u8]>>>(&mut self, item: B) { - self.got = Some(String::from_utf8(item.into().to_vec()).unwrap()); - } - } + use super::{Action, Binding}; const KEY: VirtualKeyCode = VirtualKeyCode::Key0; @@ -427,84 +383,81 @@ mod tests { { name: $name:ident, binding: $binding:expr, - expect: $expect:expr, + triggers: $triggers:expr, mode: $mode:expr, mods: $mods:expr } => { #[test] fn $name() { - let bindings = &[$binding]; - - let mut receiver = Receiver::default(); - - Processor::process_key_bindings( - bindings, $mode, &mut receiver, $mods, KEY - ); - assert_eq!(receiver.got, $expect); + if $triggers { + assert!($binding.is_triggered_by(&$mode, &$mods, &KEY)); + } else { + assert!(!$binding.is_triggered_by(&$mode, &$mods, &KEY)); + } } } } test_process_binding! { name: process_binding_nomode_shiftmod_require_shift, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::SHIFT, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }}, - expect: Some(String::from("\x1b[1;2D")), + binding: Binding { trigger: KEY, mods: mods::SHIFT, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }, + triggers: true, mode: mode::NONE, mods: mods::SHIFT } test_process_binding! { name: process_binding_nomode_nomod_require_shift, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::SHIFT, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }}, - expect: None, + binding: Binding { trigger: KEY, mods: mods::SHIFT, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }, + triggers: false, mode: mode::NONE, mods: mods::NONE } test_process_binding! { name: process_binding_nomode_controlmod, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::CONTROL, action: Action::from("\x1b[1;5D"), mode: mode::NONE, notmode: mode::NONE }}, - expect: Some(String::from("\x1b[1;5D")), + binding: Binding { trigger: KEY, mods: mods::CONTROL, action: Action::from("\x1b[1;5D"), mode: mode::NONE, notmode: mode::NONE }, + triggers: true, mode: mode::NONE, mods: mods::CONTROL } test_process_binding! { name: process_binding_nomode_nomod_require_not_appcursor, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::ANY, action: Action::from("\x1b[D"), mode: mode::NONE, notmode: mode::APP_CURSOR }}, - expect: Some(String::from("\x1b[D")), + binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1b[D"), mode: mode::NONE, notmode: mode::APP_CURSOR }, + triggers: true, mode: mode::NONE, mods: mods::NONE } test_process_binding! { name: process_binding_appcursormode_nomod_require_appcursor, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }}, - expect: Some(String::from("\x1bOD")), + binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, + triggers: true, mode: mode::APP_CURSOR, mods: mods::NONE } test_process_binding! { name: process_binding_nomode_nomod_require_appcursor, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }}, - expect: None, + binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, + triggers: false, mode: mode::NONE, mods: mods::NONE } test_process_binding! { name: process_binding_appcursormode_appkeypadmode_nomod_require_appcursor, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }}, - expect: Some(String::from("\x1bOD")), + binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, + triggers: true, mode: mode::APP_CURSOR | mode::APP_KEYPAD, mods: mods::NONE } test_process_binding! { name: process_binding_fail_with_extra_mods, - binding: KeyBinding { key: KEY, binding: Binding { mods: mods::SUPER, action: Action::from("arst"), mode: mode::NONE, notmode: mode::NONE }}, - expect: None, + binding: Binding { trigger: KEY, mods: mods::SUPER, action: Action::from("arst"), mode: mode::NONE, notmode: mode::NONE }, + triggers: false, mode: mode::NONE, mods: mods::SUPER | mods::ALT } |