add key handling by code
This commit is contained in:
parent
f0f9b83270
commit
22458ddd4a
4 changed files with 163 additions and 21 deletions
|
@ -55,6 +55,15 @@ pub enum CustomKeyHintLocation {
|
||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
|
||||||
|
pub enum KeyDetectionType {
|
||||||
|
/// Raw keyboard value, might not be correct all layouts
|
||||||
|
Code,
|
||||||
|
/// The value of the key, but note that shift+3 != 3 (as shift+3 = #)
|
||||||
|
Value,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
/// searches `$PATH` for executables and allows them to be run by selecting them.
|
/// searches `$PATH` for executables and allows them to be run by selecting them.
|
||||||
|
@ -151,6 +160,20 @@ impl FromStr for SortOrder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for KeyDetectionType {
|
||||||
|
type Err = ArgsError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"value" => Ok(KeyDetectionType::Value),
|
||||||
|
"code" => Ok(KeyDetectionType::Code),
|
||||||
|
_ => Err(ArgsError::InvalidParameter(
|
||||||
|
format!("{s} is not a valid argument, see help for details").to_owned(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Parser)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Parser)]
|
||||||
#[clap(about = "Worf is a wofi clone written in rust, it aims to be a drop-in replacement")]
|
#[clap(about = "Worf is a wofi clone written in rust, it aims to be a drop-in replacement")]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -161,7 +184,7 @@ pub struct Config {
|
||||||
|
|
||||||
/// Selects a config file to use
|
/// Selects a config file to use
|
||||||
#[clap(short = 'c', long = "conf")]
|
#[clap(short = 'c', long = "conf")]
|
||||||
config: Option<String>,
|
cfg_path: Option<String>,
|
||||||
|
|
||||||
/// Prints the version and then exits
|
/// Prints the version and then exits
|
||||||
#[clap(short = 'v', long = "version")]
|
#[clap(short = 'v', long = "version")]
|
||||||
|
@ -329,7 +352,7 @@ pub struct Config {
|
||||||
|
|
||||||
/// Orientation of items in the row box where items are displayed
|
/// Orientation of items in the row box where items are displayed
|
||||||
#[clap(long = "row-box-orientation")]
|
#[clap(long = "row-box-orientation")]
|
||||||
row_bow_orientation: Option<Orientation>,
|
row_box_orientation: Option<Orientation>,
|
||||||
|
|
||||||
#[clap(long = "line-wrap")]
|
#[clap(long = "line-wrap")]
|
||||||
line_wrap: Option<WrapMode>,
|
line_wrap: Option<WrapMode>,
|
||||||
|
@ -337,6 +360,9 @@ pub struct Config {
|
||||||
/// Display only icon in emoji mode
|
/// Display only icon in emoji mode
|
||||||
#[clap(long = "emoji-hide-string")]
|
#[clap(long = "emoji-hide-string")]
|
||||||
emoji_hide_label: Option<bool>,
|
emoji_hide_label: Option<bool>,
|
||||||
|
|
||||||
|
#[clap(long = "keyboard-detection-type")]
|
||||||
|
key_detection_type: Option<KeyDetectionType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
@ -438,7 +464,7 @@ impl Config {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn row_bow_orientation(&self) -> Orientation {
|
pub fn row_bow_orientation(&self) -> Orientation {
|
||||||
self.row_bow_orientation.unwrap_or(Orientation::Horizontal)
|
self.row_box_orientation.unwrap_or(Orientation::Horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -519,6 +545,13 @@ impl Config {
|
||||||
pub fn emoji_hide_label(&self) -> bool {
|
pub fn emoji_hide_label(&self) -> bool {
|
||||||
self.emoji_hide_label.unwrap_or(false)
|
self.emoji_hide_label.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn key_detection_type(&self) -> KeyDetectionType {
|
||||||
|
self.key_detection_type
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(KeyDetectionType::Value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_false() -> bool {
|
fn default_false() -> bool {
|
||||||
|
@ -672,7 +705,7 @@ pub fn resolve_path(
|
||||||
/// * no config file exists
|
/// * no config file exists
|
||||||
/// * config file and args cannot be merged
|
/// * config file and args cannot be merged
|
||||||
pub fn load_config(args_opt: Option<&Config>) -> Result<Config, Error> {
|
pub fn load_config(args_opt: Option<&Config>) -> Result<Config, Error> {
|
||||||
let config_path = conf_path(args_opt.as_ref().and_then(|c| c.config.as_ref()));
|
let config_path = conf_path(args_opt.as_ref().and_then(|c| c.cfg_path.as_ref()));
|
||||||
match config_path {
|
match config_path {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
let toml_content = fs::read_to_string(path).map_err(|e| Error::Io(format!("{e}")))?;
|
let toml_content = fs::read_to_string(path).map_err(|e| Error::Io(format!("{e}")))?;
|
||||||
|
|
|
@ -25,7 +25,9 @@ use gtk4_layer_shell::{Edge, KeyboardMode, LayerShell};
|
||||||
use log;
|
use log;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::config::{Anchor, Config, CustomKeyHintLocation, MatchMethod, SortOrder, WrapMode};
|
use crate::config::{
|
||||||
|
Anchor, Config, CustomKeyHintLocation, KeyDetectionType, MatchMethod, SortOrder, WrapMode,
|
||||||
|
};
|
||||||
use crate::desktop::known_image_extension_regex_pattern;
|
use crate::desktop::known_image_extension_regex_pattern;
|
||||||
use crate::{Error, config, desktop};
|
use crate::{Error, config, desktop};
|
||||||
|
|
||||||
|
@ -335,6 +337,109 @@ impl From<gdk::Key> for Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Key {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
match value {
|
||||||
|
// Letters
|
||||||
|
38 => Key::A,
|
||||||
|
56 => Key::B,
|
||||||
|
54 => Key::C,
|
||||||
|
40 => Key::D,
|
||||||
|
26 => Key::E,
|
||||||
|
41 => Key::F,
|
||||||
|
42 => Key::G,
|
||||||
|
43 => Key::H,
|
||||||
|
31 => Key::I,
|
||||||
|
44 => Key::J,
|
||||||
|
45 => Key::K,
|
||||||
|
46 => Key::L,
|
||||||
|
58 => Key::M,
|
||||||
|
57 => Key::N,
|
||||||
|
32 => Key::O,
|
||||||
|
33 => Key::P,
|
||||||
|
24 => Key::Q,
|
||||||
|
27 => Key::R,
|
||||||
|
39 => Key::S,
|
||||||
|
28 => Key::T,
|
||||||
|
30 => Key::U,
|
||||||
|
55 => Key::V,
|
||||||
|
25 => Key::W,
|
||||||
|
53 => Key::X,
|
||||||
|
29 => Key::Y,
|
||||||
|
52 => Key::Z,
|
||||||
|
|
||||||
|
// Numbers
|
||||||
|
10 => Key::Num0,
|
||||||
|
11 => Key::Num1,
|
||||||
|
12 => Key::Num2,
|
||||||
|
13 => Key::Num3,
|
||||||
|
14 => Key::Num4,
|
||||||
|
15 => Key::Num5,
|
||||||
|
16 => Key::Num6,
|
||||||
|
17 => Key::Num7,
|
||||||
|
18 => Key::Num8,
|
||||||
|
19 => Key::Num9,
|
||||||
|
|
||||||
|
// Function Keys
|
||||||
|
67 => Key::F1,
|
||||||
|
68 => Key::F2,
|
||||||
|
69 => Key::F3,
|
||||||
|
70 => Key::F4,
|
||||||
|
71 => Key::F5,
|
||||||
|
72 => Key::F6,
|
||||||
|
73 => Key::F7,
|
||||||
|
74 => Key::F8,
|
||||||
|
75 => Key::F9,
|
||||||
|
76 => Key::F10,
|
||||||
|
77 => Key::F11,
|
||||||
|
78 => Key::F12,
|
||||||
|
|
||||||
|
// Navigation / Editing
|
||||||
|
9 => Key::Escape,
|
||||||
|
36 => Key::Enter,
|
||||||
|
65 => Key::Space,
|
||||||
|
23 => Key::Tab,
|
||||||
|
22 => Key::Backspace,
|
||||||
|
118 => Key::Insert,
|
||||||
|
119 => Key::Delete,
|
||||||
|
110 => Key::Home,
|
||||||
|
115 => Key::End,
|
||||||
|
112 => Key::PageUp,
|
||||||
|
117 => Key::PageDown,
|
||||||
|
113 => Key::Left,
|
||||||
|
114 => Key::Right,
|
||||||
|
111 => Key::Up,
|
||||||
|
116 => Key::Down,
|
||||||
|
|
||||||
|
// Special characters
|
||||||
|
20 => Key::Exclamation,
|
||||||
|
63 => Key::At,
|
||||||
|
3 => Key::Hash,
|
||||||
|
4 => Key::Dollar,
|
||||||
|
5 => Key::Percent,
|
||||||
|
6 => Key::Caret,
|
||||||
|
7 => Key::Ampersand,
|
||||||
|
8 => Key::Asterisk,
|
||||||
|
34 => Key::LeftParen,
|
||||||
|
35 => Key::RightParen,
|
||||||
|
48 => Key::Minus,
|
||||||
|
47 => Key::Underscore,
|
||||||
|
21 => Key::Equal,
|
||||||
|
49 => Key::Plus,
|
||||||
|
51 => Key::Backslash,
|
||||||
|
94 => Key::Pipe,
|
||||||
|
50 => Key::Quote,
|
||||||
|
59 => Key::Comma,
|
||||||
|
60 => Key::Period,
|
||||||
|
61 => Key::Slash,
|
||||||
|
62 => Key::Question,
|
||||||
|
96 => Key::Grave,
|
||||||
|
97 => Key::Tilde,
|
||||||
|
_ => Key::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Modifier {
|
pub enum Modifier {
|
||||||
Shift,
|
Shift,
|
||||||
|
@ -771,8 +876,6 @@ fn build_ui_from_menu_items<T: Clone + 'static + Send>(
|
||||||
let meta_clone = Rc::<MetaData<T>>::clone(meta);
|
let meta_clone = Rc::<MetaData<T>>::clone(meta);
|
||||||
let ui_clone = Rc::<UiElements<T>>::clone(ui);
|
let ui_clone = Rc::<UiElements<T>>::clone(ui);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
glib::idle_add_local(move || {
|
glib::idle_add_local(move || {
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
{
|
{
|
||||||
|
@ -801,10 +904,9 @@ fn build_ui_from_menu_items<T: Clone + 'static + Send>(
|
||||||
sort_flow_box_childs(child1, child2, &items_sort)
|
sort_flow_box_childs(child1, child2, &items_sort)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if done {
|
if done {
|
||||||
let lock = ui_clone.menu_rows.read().unwrap();
|
let lock = ui_clone.menu_rows.read().unwrap();
|
||||||
|
|
||||||
select_first_visible_child(&lock, &ui_clone.main_box);
|
select_first_visible_child(&lock, &ui_clone.main_box);
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
@ -830,11 +932,12 @@ fn setup_key_event_handler<T: Clone + 'static + Send>(
|
||||||
let ui_clone = Rc::clone(ui);
|
let ui_clone = Rc::clone(ui);
|
||||||
let meta_clone = Rc::clone(meta);
|
let meta_clone = Rc::clone(meta);
|
||||||
let keys_clone = custom_keys.cloned();
|
let keys_clone = custom_keys.cloned();
|
||||||
key_controller.connect_key_pressed(move |_, key_value, _, modifier| {
|
key_controller.connect_key_pressed(move |_, key_value, key_code, modifier| {
|
||||||
handle_key_press(
|
handle_key_press(
|
||||||
&ui_clone,
|
&ui_clone,
|
||||||
&meta_clone,
|
&meta_clone,
|
||||||
key_value,
|
key_value,
|
||||||
|
key_code,
|
||||||
modifier,
|
modifier,
|
||||||
keys_clone.as_ref(),
|
keys_clone.as_ref(),
|
||||||
)
|
)
|
||||||
|
@ -848,6 +951,7 @@ fn handle_key_press<T: Clone + 'static + Send>(
|
||||||
ui: &Rc<UiElements<T>>,
|
ui: &Rc<UiElements<T>>,
|
||||||
meta: &Rc<MetaData<T>>,
|
meta: &Rc<MetaData<T>>,
|
||||||
keyboard_key: gdk4::Key,
|
keyboard_key: gdk4::Key,
|
||||||
|
key_code: u32,
|
||||||
modifier_type: gdk4::ModifierType,
|
modifier_type: gdk4::ModifierType,
|
||||||
custom_keys: Option<&CustomKeys>,
|
custom_keys: Option<&CustomKeys>,
|
||||||
) -> Propagation {
|
) -> Propagation {
|
||||||
|
@ -873,10 +977,13 @@ fn handle_key_press<T: Clone + 'static + Send>(
|
||||||
if let Some(custom_keys) = custom_keys {
|
if let Some(custom_keys) = custom_keys {
|
||||||
let mods = modifiers_from_mask(modifier_type);
|
let mods = modifiers_from_mask(modifier_type);
|
||||||
for custom_key in &custom_keys.bindings {
|
for custom_key in &custom_keys.bindings {
|
||||||
log::debug!(
|
let custom_key_match = if meta.config.key_detection_type() == KeyDetectionType::Code {
|
||||||
"comparing custom key {custom_key:?} to mask {mods:?} and key {keyboard_key}"
|
custom_key.key == key_code.into()
|
||||||
);
|
} else {
|
||||||
if custom_key.key == keyboard_key.to_upper().into() && mods.is_subset(&custom_key.modifiers) {
|
custom_key.key == keyboard_key.to_upper().into()
|
||||||
|
} && mods.is_subset(&custom_key.modifiers);
|
||||||
|
|
||||||
|
if custom_key_match {
|
||||||
let search_lock = ui.search_text.lock().unwrap();
|
let search_lock = ui.search_text.lock().unwrap();
|
||||||
if let Err(e) = handle_selected_item(
|
if let Err(e) = handle_selected_item(
|
||||||
ui,
|
ui,
|
||||||
|
|
|
@ -11,19 +11,22 @@ pub(crate) struct EmojiProvider<T: Clone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> EmojiProvider<T> {
|
impl<T: Clone> EmojiProvider<T> {
|
||||||
pub(crate) fn new(data: T, sort_order: &SortOrder, hide_label: &bool) -> Self {
|
pub(crate) fn new(data: T, sort_order: &SortOrder, hide_label: bool) -> Self {
|
||||||
let emoji = emoji::search::search_annotation_all("");
|
let emoji = emoji::search::search_annotation_all("");
|
||||||
let mut menus = emoji
|
let mut menus = emoji
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| {
|
.map(|e| {
|
||||||
MenuItem::new(
|
MenuItem::new(
|
||||||
if *hide_label {
|
if hide_label {
|
||||||
e.glyph.to_string()
|
e.glyph.to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("{} — Category: {} — Name: {}", e.glyph, e.group, e.name)
|
format!("{} — Category: {} — Name: {}", e.glyph, e.group, e.name)
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
Some(format!("emoji {} — Category: {} — Name: {}", e.glyph, e.group, e.name)),
|
Some(format!(
|
||||||
|
"emoji {} — Category: {} — Name: {}",
|
||||||
|
e.glyph, e.group, e.name
|
||||||
|
)),
|
||||||
vec![],
|
vec![],
|
||||||
None,
|
None,
|
||||||
0.0,
|
0.0,
|
||||||
|
@ -55,7 +58,7 @@ impl<T: Clone> ItemProvider<T> for EmojiProvider<T> {
|
||||||
///
|
///
|
||||||
/// Forwards errors from the gui. See `gui::show` for details.
|
/// Forwards errors from the gui. See `gui::show` for details.
|
||||||
pub fn show(config: &Config) -> Result<(), Error> {
|
pub fn show(config: &Config) -> Result<(), Error> {
|
||||||
let provider = EmojiProvider::new(0, &config.sort_order(), &config.emoji_hide_label());
|
let provider = EmojiProvider::new(0, &config.sort_order(), config.emoji_hide_label());
|
||||||
let selection_result = gui::show(config.clone(), provider, true, None, None)?;
|
let selection_result = gui::show(config.clone(), provider, true, None, None)?;
|
||||||
match selection_result.menu.action {
|
match selection_result.menu.action {
|
||||||
None => Err(Error::MissingAction),
|
None => Err(Error::MissingAction),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use regex::Regex;
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::gui;
|
use crate::gui;
|
||||||
use crate::gui::{ItemProvider, MenuItem};
|
use crate::gui::{ItemProvider, MenuItem};
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct MathProvider<T: Clone> {
|
pub(crate) struct MathProvider<T: Clone> {
|
||||||
|
@ -25,7 +25,6 @@ impl<T: Clone> ItemProvider<T> for MathProvider<T> {
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
|
fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
|
||||||
if let Some(search_text) = search {
|
if let Some(search_text) = search {
|
||||||
|
|
||||||
let re = Regex::new(r"0x[0-9a-fA-F]+").unwrap();
|
let re = Regex::new(r"0x[0-9a-fA-F]+").unwrap();
|
||||||
let result = re.replace_all(search_text, |caps: ®ex::Captures| {
|
let result = re.replace_all(search_text, |caps: ®ex::Captures| {
|
||||||
let hex_str = &caps[0][2..]; // Skip "0x"
|
let hex_str = &caps[0][2..]; // Skip "0x"
|
||||||
|
|
Loading…
Add table
Reference in a new issue