Add `ReceiveChar` action for passing key's text

This commit is contained in:
zsugabubus 2019-09-28 00:37:22 +00:00 committed by Christian Duerr
parent 87cf14a4b7
commit fe6f1760b4
4 changed files with 55 additions and 69 deletions

View File

@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Extra bindings for F13-F20 - Extra bindings for F13-F20
- Terminal escape bindings with combined modifiers - Terminal escape bindings with combined modifiers
- Bindings for ScrollToTop and ScrollToBottom actions - Bindings for ScrollToTop and ScrollToBottom actions
- `ReceiveChar` key binding action to insert the key's text character
### Changed ### Changed

View File

@ -496,6 +496,7 @@ mouse_bindings:
# - ToggleFullscreen # - ToggleFullscreen
# - SpawnNewInstance # - SpawnNewInstance
# - ClearLogNotice # - ClearLogNotice
# - ReceiveChar
# - None # - None
# #
# (macOS only): # (macOS only):
@ -536,7 +537,8 @@ mouse_bindings:
# #
# Bindings are always filled by default, but will be replaced when a new # Bindings are always filled by default, but will be replaced when a new
# binding with the same triggers is defined. To unset a default binding, it can # binding with the same triggers is defined. To unset a default binding, it can
# be mapped to the `None` action. # be mapped to the `ReceiveChar` action. Alternatively, you can use `None` for
# a no-op if you do not wish to receive input characters for that binding.
key_bindings: key_bindings:
# (Windows/Linux only) # (Windows/Linux only)
#- { key: V, mods: Control|Shift, action: Paste } #- { key: V, mods: Control|Shift, action: Paste }

View File

@ -269,6 +269,7 @@ where
{ {
let mut bindings: Vec<Binding<T>> = failure_default(deserializer)?; let mut bindings: Vec<Binding<T>> = failure_default(deserializer)?;
// Remove matching default bindings
for binding in bindings.iter() { for binding in bindings.iter() {
default.retain(|b| !b.triggers_match(binding)); default.retain(|b| !b.triggers_match(binding));
} }

View File

@ -234,60 +234,60 @@ impl<T> Binding<T> {
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub enum Action { pub enum Action {
/// Write an escape sequence /// Write an escape sequence.
#[serde(skip)] #[serde(skip)]
Esc(String), Esc(String),
/// Paste contents of system clipboard /// Paste contents of system clipboard.
Paste, Paste,
// Store current selection into clipboard /// Store current selection into clipboard.
Copy, Copy,
/// Paste contents of selection buffer /// Paste contents of selection buffer.
PasteSelection, PasteSelection,
/// Increase font size /// Increase font size.
IncreaseFontSize, IncreaseFontSize,
/// Decrease font size /// Decrease font size.
DecreaseFontSize, DecreaseFontSize,
/// Reset font size to the config value /// Reset font size to the config value.
ResetFontSize, ResetFontSize,
/// Scroll exactly one page up /// Scroll exactly one page up.
ScrollPageUp, ScrollPageUp,
/// Scroll exactly one page down /// Scroll exactly one page down.
ScrollPageDown, ScrollPageDown,
/// Scroll one line up /// Scroll one line up.
ScrollLineUp, ScrollLineUp,
/// Scroll one line down /// Scroll one line down.
ScrollLineDown, ScrollLineDown,
/// Scroll all the way to the top /// Scroll all the way to the top.
ScrollToTop, ScrollToTop,
/// Scroll all the way to the bottom /// Scroll all the way to the bottom.
ScrollToBottom, ScrollToBottom,
/// Clear the display buffer(s) to remove history /// Clear the display buffer(s) to remove history.
ClearHistory, ClearHistory,
/// Run given command /// Run given command.
#[serde(skip)] #[serde(skip)]
Command(String, Vec<String>), Command(String, Vec<String>),
/// Hides the Alacritty window /// Hide the Alacritty window.
Hide, Hide,
/// Quits Alacritty. /// Quit Alacritty.
Quit, Quit,
/// Clears warning and error notices. /// Clear warning and error notices.
ClearLogNotice, ClearLogNotice,
/// Spawn a new instance of Alacritty. /// Spawn a new instance of Alacritty.
@ -300,6 +300,9 @@ pub enum Action {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
ToggleSimpleFullscreen, ToggleSimpleFullscreen,
/// Allow receiving char input.
ReceiveChar,
/// No action. /// No action.
None, None,
} }
@ -389,7 +392,7 @@ impl Action {
Action::SpawnNewInstance => { Action::SpawnNewInstance => {
ctx.spawn_new_instance(); ctx.spawn_new_instance();
}, },
Action::None => (), Action::ReceiveChar | Action::None => (),
} }
} }
@ -714,7 +717,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
self.copy_selection(); self.copy_selection();
} }
// Spawn URL launcher when clicking on URLs /// Spawn URL launcher when clicking on URLs.
fn launch_url(&self, url: Url) { fn launch_url(&self, url: Url) {
if self.ctx.mouse().block_url_launcher { if self.ctx.mouse().block_url_launcher {
return; return;
@ -844,7 +847,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
self.ctx.mouse_mut().last_button = button; self.ctx.mouse_mut().last_button = button;
} }
/// Process key input /// Process key input.
pub fn process_key(&mut self, input: KeyboardInput) { pub fn process_key(&mut self, input: KeyboardInput) {
self.ctx.modifiers().update(input); self.ctx.modifiers().update(input);
@ -862,17 +865,13 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
match input.state { match input.state {
ElementState::Pressed => { ElementState::Pressed => {
*self.ctx.received_count() = 0; *self.ctx.received_count() = 0;
*self.ctx.suppress_chars() = false; self.process_key_bindings(input);
if self.process_key_bindings(input) {
*self.ctx.suppress_chars() = true;
}
}, },
ElementState::Released => *self.ctx.suppress_chars() = false, ElementState::Released => *self.ctx.suppress_chars() = false,
} }
} }
/// Process a received character /// Process a received character.
pub fn received_char(&mut self, c: char) { pub fn received_char(&mut self, c: char) {
if *self.ctx.suppress_chars() { if *self.ctx.suppress_chars() {
return; return;
@ -901,55 +900,41 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
*self.ctx.received_count() += 1; *self.ctx.received_count() += 1;
} }
/// Attempts to find a binding and execute its action /// Attempt to find a binding and execute its action.
/// ///
/// The provided mode, mods, and key must match what is allowed by a binding /// The provided mode, mods, and key must match what is allowed by a binding
/// for its action to be executed. /// for its action to be executed.
/// fn process_key_bindings(&mut self, input: KeyboardInput) {
/// Returns true if an action is executed. let mode = *self.ctx.terminal().mode();
fn process_key_bindings(&mut self, input: KeyboardInput) -> bool {
let mut has_binding = false;
for binding in self.key_bindings {
let is_triggered = match binding.trigger {
Key::Scancode(_) => binding.is_triggered_by(
*self.ctx.terminal().mode(),
input.modifiers,
&Key::Scancode(input.scancode),
false,
),
_ => {
if let Some(key) = input.virtual_keycode {
let key = Key::from_glutin_input(key);
binding.is_triggered_by(
*self.ctx.terminal().mode(),
input.modifiers,
&key,
false,
)
} else {
false
}
},
};
if is_triggered { *self.ctx.suppress_chars() = self
// binding was triggered; run the action .key_bindings
.iter()
.filter(|binding| {
let key = match (binding.trigger, input.virtual_keycode) {
(Key::Scancode(_), _) => Key::Scancode(input.scancode),
(_, Some(key)) => Key::from_glutin_input(key),
_ => return false,
};
binding.is_triggered_by(mode, input.modifiers, &key, false)
})
.fold(None, |suppress_chars, binding| {
// Binding was triggered; run the action
binding.execute(&mut self.ctx, false); binding.execute(&mut self.ctx, false);
has_binding = true;
}
}
has_binding // Don't suppress when there has been a `ReceiveChar` action
Some(suppress_chars.unwrap_or(true) && binding.action != Action::ReceiveChar)
})
// Don't suppress char if no bindings were triggered
.unwrap_or(false);
} }
/// Attempts to find a binding and execute its action /// Attempt to find a binding and execute its action.
/// ///
/// The provided mode, mods, and key must match what is allowed by a binding /// The provided mode, mods, and key must match what is allowed by a binding
/// for its action to be executed. /// for its action to be executed.
/// fn process_mouse_bindings(&mut self, mods: ModifiersState, button: MouseButton) {
/// Returns true if an action is executed.
fn process_mouse_bindings(&mut self, mods: ModifiersState, button: MouseButton) -> bool {
let mut has_binding = false;
for binding in self.mouse_bindings { for binding in self.mouse_bindings {
if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) { if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) {
// binding was triggered; run the action // binding was triggered; run the action
@ -960,11 +945,8 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
| TermMode::MOUSE_MOTION, | TermMode::MOUSE_MOTION,
); );
binding.execute(&mut self.ctx, mouse_mode); binding.execute(&mut self.ctx, mouse_mode);
has_binding = true;
} }
} }
has_binding
} }
/// Return the message bar's message if there is some at the specified point /// Return the message bar's message if there is some at the specified point