Skip to content

Commit

Permalink
Implement setting mappings from config
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewRadev committed Feb 10, 2022
1 parent 9fe40f4 commit dff9052
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 7 deletions.
15 changes: 15 additions & 0 deletions res/default_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,18 @@ zoom: 1.0
# - ["alacritty", "-e", "vim", "{path}"]
#
editor_command: ["gvim", "{path}"]

# You can set your own keybindings, or unset the defaults by setting them to
# "Noop". See the API documentaiton for a full list of actions, under
# `ui::action::Action`.
#
# The keybindings are passed along to GDK:
# - if given `key_char`, it's passed along to `Key::from_unicode`
# - if given `key_name`, `Key::from_name` is called with it. So, "plus"
# instead of "+".
# - Modifiers that are supported: "control", "shift", "alt"
#
mappings: []
# mappings:
# - { key_char: "q", modifiers: [], action: "Quit" }
# - { key_name: "minus", modifiers: ["control"], action: "ZoomOut" }
21 changes: 20 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use structopt::StructOpt;
use tempfile::NamedTempFile;

use crate::assets::HIGHLIGHT_JS_VERSION;
use crate::ui::action::Action;

/// Command-line options. Managed by StructOpt.
#[derive(Debug, StructOpt)]
Expand Down Expand Up @@ -94,6 +95,7 @@ impl Options {
/// config directory named "config.yaml".
///
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Config {
/// The zoom level of the page. Defaults to 1.0, but on a HiDPI screen should be set to a
/// higher value.
Expand All @@ -104,11 +106,28 @@ pub struct Config {
/// which will produce a command-line warning when it's attempted.
///
pub editor_command: Vec<String>,

/// Custom mappings. Each entry can contain three keys:
///
/// - `key_char` or `key_name`: A descriptor, passed along to [`gdk::keys::Key::from_unicode`]
/// or [`gdk::keys::Key::from_name`] respectively.
/// - `modifiers`: A list of modifiers, either "control", "shift", or "alt".
/// - `action`: See [`crate::ui::action::Action`].
///
pub mappings: Vec<MappingDefinition>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MappingDefinition {
pub key_char: Option<char>,
pub key_name: Option<String>,
pub modifiers: Vec<String>,
pub action: Action,
}

impl Default for Config {
fn default() -> Self {
Self { zoom: 1.0, editor_command: Vec::new() }
Self { zoom: 1.0, editor_command: Vec::new(), mappings: Vec::new() }
}
}

Expand Down
38 changes: 37 additions & 1 deletion src/ui/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
use std::collections::HashMap;

use anyhow::anyhow;
use gdk::ModifierType;
use gdk::keys::{self, Key};
use serde::{Serialize, Deserialize};

use crate::input::MappingDefinition;

/// Mappable actions
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Action {
/// Placeholder action to allow unmapping keys
Noop,
Expand Down Expand Up @@ -49,6 +53,7 @@ pub enum Action {
/// 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>,
}
Expand Down Expand Up @@ -86,6 +91,37 @@ impl Keymaps {
Self { mappings: HashMap::new() }
}

/// Parse the given mappings as described in [`crate::input::Config`]
///
pub fn add_config_mappings(&mut self, mappings: &Vec<MappingDefinition>) -> anyhow::Result<()> {
for mapping in mappings {
let mut modifiers = ModifierType::empty();
for m in &mapping.modifiers {
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)); }
},
}
}

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());
}

Ok(())
}

/// Get the action corresponding to the given modifiers and key. Uppercase unicode letters like
/// are normalized to a lowercase letter + shift.
///
Expand Down
15 changes: 10 additions & 5 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::path::{PathBuf, Path};
use std::process::Command;

use gtk::prelude::*;
use log::{debug, warn};
use log::{debug, warn, error};
use pathbuftools::PathBufTools;

use crate::assets::Assets;
Expand Down Expand Up @@ -107,14 +107,19 @@ impl App {
let filename = self.filename.clone();
let editor_command = self.config.editor_command.clone();

let mut keymaps = Keymaps::default();
keymaps.add_config_mappings(&self.config.mappings).unwrap_or_else(|e| {
error!("Mapping parsing error: {}", e);
});

// Key presses mapped to repeatable events:
let browser = self.browser.clone();
let keymaps = Keymaps::default();
let keymaps_clone = keymaps.clone();
self.window.connect_key_press_event(move |_window, event| {
let keyval = event.keyval();
let keystate = event.state();

match keymaps.get_action(keystate, keyval) {
match keymaps_clone.get_action(keystate, keyval) {
Action::SmallScrollDown => browser.execute_js("window.scrollBy(0, 70)"),
Action::BigScrollDown => browser.execute_js("window.scrollBy(0, 250)"),
Action::SmallScrollUp => browser.execute_js("window.scrollBy(0, -70)"),
Expand All @@ -130,12 +135,12 @@ impl App {

// Key releases mapped to one-time events:
let browser = self.browser.clone();
let keymaps = Keymaps::default();
let keymaps_clone = keymaps.clone();
self.window.connect_key_release_event(move |window, event| {
let keyval = event.keyval();
let keystate = event.state();

match keymaps.get_action(keystate, keyval) {
match keymaps_clone.get_action(keystate, keyval) {
Action::LaunchEditor => {
debug!("Launching an editor");
launch_editor(&editor_command, &filename);
Expand Down

0 comments on commit dff9052

Please sign in to comment.