-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* custom-mappings: Minor renaming and documentation tweaks Implement Default to enable skipping fields in mapping defs Add some debug logging for mapping definitions Tests for defining mappings Minor rewording Apply minor clippy fix Some renaming, restructuring, and documentation Implement setting mappings from config Fix logging bug Fix "upper" key handling README updates Extract all keybindings to the `Keymaps` struct Create a dedicated "Keymaps" structure Gitignore cargo config Extract browser and file picker into separate UI modules
- Loading branch information
Showing
14 changed files
with
575 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,6 @@ | |
/.cargo/config.toml | ||
|
||
notes | ||
|
||
# Ignored since it contains system-specific linker config | ||
/.cargo/config.toml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
//! Actions on the UI triggered by keybindings. | ||
use std::collections::HashMap; | ||
|
||
use anyhow::anyhow; | ||
use gdk::ModifierType; | ||
use gdk::keys::{self, Key}; | ||
use log::debug; | ||
use serde::{Serialize, Deserialize}; | ||
|
||
use crate::input::MappingDefinition; | ||
|
||
/// Mappable actions | ||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | ||
pub enum Action { | ||
/// Placeholder action to allow unmapping keys | ||
Noop, | ||
|
||
/// Scroll up by a small step. Default: `k` | ||
SmallScrollUp, | ||
/// Scroll down by a small step. Default: `j` | ||
SmallScrollDown, | ||
|
||
/// Scroll up by a large step. Default: `K` | ||
BigScrollUp, | ||
/// Scroll down by a large step. Default: `J` | ||
BigScrollDown, | ||
|
||
/// Scroll to the top of the document. Default: `g` | ||
ScrollToTop, | ||
/// Scroll to the bottom of the document. Default: `G` | ||
ScrollToBottom, | ||
|
||
/// Quit the entire application. Default: `ctrl+q` | ||
Quit, | ||
|
||
/// Launch an editor instance if it's configured. Default: `e` | ||
LaunchEditor, | ||
/// Exec the current process into an editor instance if it's configured (and it's possible on | ||
/// the OS). Default: `E` | ||
ExecEditor, | ||
|
||
/// Zoom the browser in by 10%. Default: `+` | ||
ZoomIn, | ||
/// Zoom the browser out by 10%. Default: `-` | ||
ZoomOut, | ||
/// Reset the zoom level to the configured starting point. Default: `=` | ||
ZoomReset, | ||
|
||
/// Show a help popup. Default: `F1` | ||
ShowHelp, | ||
} | ||
|
||
impl Default for Action { | ||
fn default() -> Self { | ||
Action::Noop | ||
} | ||
} | ||
|
||
/// A mapping from key bindings to all the different UI actions. Initialized with a full set of | ||
/// defaults, which can be overridden by configuration. | ||
/// | ||
#[derive(Clone)] | ||
pub struct Keymaps { | ||
mappings: HashMap<(ModifierType, Key), Action>, | ||
} | ||
|
||
impl Default for Keymaps { | ||
fn default() -> Self { | ||
let mut keymaps = Self::new(); | ||
|
||
// Scroll with j/k, J/K: | ||
keymaps.set_action(ModifierType::empty(), keys::constants::j, Action::SmallScrollDown); | ||
keymaps.set_action(ModifierType::empty(), keys::constants::k, Action::SmallScrollUp); | ||
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::j, Action::BigScrollDown); | ||
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::k, Action::BigScrollUp); | ||
// Jump to the top/bottom with g/G | ||
keymaps.set_action(ModifierType::empty(), keys::constants::g, Action::ScrollToTop); | ||
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::g, Action::ScrollToBottom); | ||
// Ctrl+Q to quit | ||
keymaps.set_action(ModifierType::CONTROL_MASK, keys::constants::q, Action::Quit); | ||
// e, E for editor integration | ||
keymaps.set_action(ModifierType::empty(), keys::constants::e, Action::LaunchEditor); | ||
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::e, Action::ExecEditor); | ||
// +/-/= for zoom control | ||
keymaps.set_action(ModifierType::empty(), keys::constants::plus, Action::ZoomIn); | ||
keymaps.set_action(ModifierType::empty(), keys::constants::minus, Action::ZoomOut); | ||
keymaps.set_action(ModifierType::empty(), keys::constants::equal, Action::ZoomReset); | ||
// F1 to show help popup | ||
keymaps.set_action(ModifierType::empty(), keys::constants::F1, Action::ShowHelp); | ||
|
||
keymaps | ||
} | ||
} | ||
|
||
impl Keymaps { | ||
/// Create an empty set of keymaps. | ||
/// | ||
pub fn new() -> Self { | ||
Self { mappings: HashMap::new() } | ||
} | ||
|
||
/// Parse the given mappings as described in [`crate::input::Config`] | ||
/// | ||
pub fn add_config_mappings(&mut self, mappings: &[MappingDefinition]) -> anyhow::Result<()> { | ||
for mapping in mappings { | ||
let mut modifiers = ModifierType::empty(); | ||
for m in &mapping.mods { | ||
match m.as_str() { | ||
"control" => { modifiers |= ModifierType::CONTROL_MASK; } | ||
"shift" => { modifiers |= ModifierType::SHIFT_MASK; } | ||
"alt" => { modifiers |= ModifierType::MOD1_MASK; } | ||
_ => { | ||
{ return Err(anyhow!("Unknown modifier: {}", m)); } | ||
}, | ||
} | ||
} | ||
|
||
if mapping.key_char.is_some() && mapping.key_name.is_some() { | ||
return Err( | ||
anyhow!("Both `key_char` or `key_name` given, please pick just one: {:?}", mapping) | ||
); | ||
} | ||
|
||
let key = | ||
if let Some(c) = mapping.key_char { | ||
Key::from_unicode(c) | ||
} else if let Some(name) = &mapping.key_name { | ||
Key::from_name(name) | ||
} else { | ||
return Err(anyhow!("No `key_char` or `key_name` given: {:?}", mapping)); | ||
}; | ||
|
||
self.set_action(modifiers, key, mapping.action.clone()); | ||
debug!("Defined custom mapping: {:?}", mapping); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Get the action corresponding to the given modifiers and key. Uppercase unicode letters like | ||
/// are normalized to a lowercase letter + shift. | ||
/// | ||
pub fn get_action(&self, modifiers: ModifierType, key: Key) -> Action { | ||
let (key, modifiers) = Self::normalize_input(key, modifiers); | ||
self.mappings.get(&(modifiers, key)).cloned().unwrap_or(Action::Noop) | ||
} | ||
|
||
/// Set the action corresponding to the given modifiers and key. Could override existing | ||
/// actions. Setting `Action::Noop` is the way to "unmap" a keybinding. Uppercase unicode | ||
/// letters like are normalized to a lowercase letter + shift. | ||
/// | ||
pub fn set_action(&mut self, modifiers: ModifierType, key: Key, action: Action) { | ||
let (key, modifiers) = Self::normalize_input(key, modifiers); | ||
self.mappings.insert((modifiers, key), action); | ||
} | ||
|
||
fn normalize_input(mut key: Key, mut modifiers: ModifierType) -> (Key, ModifierType) { | ||
// If we get something considered an "upper" key, that means shift is being held. This is | ||
// not just for A -> S-a, but also for + -> = (though the + is not transformed). | ||
if key.is_upper() { | ||
key = key.to_lower(); | ||
modifiers.insert(ModifierType::SHIFT_MASK); | ||
} | ||
|
||
(key, modifiers) | ||
} | ||
} |
Oops, something went wrong.