mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-11 13:51:01 -05:00
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.
This commit is contained in:
parent
358c9fa17d
commit
d28a734473
2 changed files with 67 additions and 118 deletions
|
@ -383,14 +383,12 @@ struct RawBinding {
|
|||
impl RawBinding {
|
||||
fn into_mouse_binding(self) -> ::std::result::Result<MouseBinding, Self> {
|
||||
if self.mouse.is_some() {
|
||||
Ok(MouseBinding {
|
||||
button: self.mouse.unwrap(),
|
||||
binding: Binding {
|
||||
mods: self.mods,
|
||||
action: self.action,
|
||||
mode: self.mode,
|
||||
notmode: self.notmode,
|
||||
}
|
||||
Ok(Binding {
|
||||
trigger: self.mouse.unwrap(),
|
||||
mods: self.mods,
|
||||
action: self.action,
|
||||
mode: self.mode,
|
||||
notmode: self.notmode,
|
||||
})
|
||||
} else {
|
||||
Err(self)
|
||||
|
@ -400,13 +398,11 @@ impl RawBinding {
|
|||
fn into_key_binding(self) -> ::std::result::Result<KeyBinding, Self> {
|
||||
if self.key.is_some() {
|
||||
Ok(KeyBinding {
|
||||
key: self.key.unwrap(),
|
||||
binding: Binding {
|
||||
mods: self.mods,
|
||||
action: self.action,
|
||||
mode: self.mode,
|
||||
notmode: self.notmode,
|
||||
}
|
||||
trigger: self.key.unwrap(),
|
||||
mods: self.mods,
|
||||
action: self.action,
|
||||
mode: self.mode,
|
||||
notmode: self.notmode,
|
||||
})
|
||||
} else {
|
||||
Err(self)
|
||||
|
|
159
src/input.rs
159
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,
|
||||
|
||||
/// 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 KeyBinding {
|
||||
pub key: VirtualKeyCode,
|
||||
pub binding: Binding,
|
||||
}
|
||||
/// Bindings that are triggered by a keyboard key
|
||||
pub type KeyBinding = Binding<VirtualKeyCode>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MouseBinding {
|
||||
pub button: MouseButton,
|
||||
pub binding: Binding,
|
||||
}
|
||||
/// 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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn execute<'a, N: Notify>(&self, ctx: &mut ActionContext<'a, N>) {
|
||||
self.binding.action.execute(ctx)
|
||||
self.trigger == *input &&
|
||||
self.mode_matches(mode) &&
|
||||
self.not_mode_matches(mode) &&
|
||||
self.mods_match(mods)
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseBinding {
|
||||
impl<T> Binding<T> {
|
||||
/// Execute the action associate with this binding
|
||||
#[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 execute<'a, N: Notify>(&self, ctx: &mut ActionContext<'a, N>) {
|
||||
self.action.execute(ctx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn execute<'a, N: Notify>(&self, ctx: &mut ActionContext<'a, N>) {
|
||||
self.binding.action.execute(ctx)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue