feat(core): implement hotkey detection for builtin matches and wire up search

This commit is contained in:
Federico Terzi 2021-08-13 21:19:46 +02:00
parent fa2709d43b
commit ba4f9400ea
14 changed files with 123 additions and 13 deletions

View File

@ -39,6 +39,7 @@ pub fn create_match_paste_active_config_info() -> BuiltInMatch {
force_mode: None, force_mode: None,
}) })
}, },
..Default::default()
} }
} }
@ -62,5 +63,6 @@ pub fn create_match_paste_active_app_info() -> BuiltInMatch {
force_mode: None, force_mode: None,
}) })
}, },
..Default::default()
} }
} }

View File

@ -34,17 +34,33 @@ pub struct BuiltInMatch {
pub id: i32, pub id: i32,
pub label: &'static str, pub label: &'static str,
pub triggers: Vec<String>, pub triggers: Vec<String>,
pub hotkey: Option<String>,
pub action: fn(context: &dyn Context) -> EventType, pub action: fn(context: &dyn Context) -> EventType,
} }
impl Default for BuiltInMatch {
fn default() -> Self {
Self {
id: 0,
label: "",
triggers: Vec::new(),
hotkey: None,
action: |_| EventType::NOOP,
}
}
}
pub fn get_builtin_matches(config: &dyn Config) -> Vec<BuiltInMatch> { pub fn get_builtin_matches(config: &dyn Config) -> Vec<BuiltInMatch> {
let mut matches = vec![ let mut matches = vec![
debug::create_match_paste_active_config_info(), debug::create_match_paste_active_config_info(),
debug::create_match_paste_active_app_info(), debug::create_match_paste_active_app_info(),
]; ];
if let Some(search_trigger) = config.search_trigger() { if config.search_trigger().is_some() || config.search_shortcut().is_some() {
matches.push(search::create_match_trigger_search_bar(&search_trigger)); matches.push(search::create_match_trigger_search_bar(
config.search_trigger(),
config.search_shortcut(),
));
} }
matches matches

View File

@ -21,13 +21,15 @@ use crate::{cli::worker::builtin::generate_next_builtin_id, engine::event::{Even
use super::BuiltInMatch; use super::BuiltInMatch;
pub fn create_match_trigger_search_bar(shortcut: &str) -> BuiltInMatch { pub fn create_match_trigger_search_bar(trigger: Option<String>, hotkey: Option<String>) -> BuiltInMatch {
BuiltInMatch { BuiltInMatch {
id: generate_next_builtin_id(), id: generate_next_builtin_id(),
label: "Open search bar", label: "Open search bar",
triggers: vec![shortcut.to_string()], triggers: trigger.map(|trigger| vec![trigger]).unwrap_or_default(),
hotkey,
action: |_| { action: |_| {
EventType::ShowSearchBar EventType::ShowSearchBar
}, },
..Default::default()
} }
} }

View File

@ -18,9 +18,9 @@
*/ */
use crossbeam::channel::{Receiver, Select, SelectedOperation}; use crossbeam::channel::{Receiver, Select, SelectedOperation};
use espanso_detect::event::InputEvent; use espanso_detect::event::{InputEvent};
use crate::engine::{event::{Event, EventType, SourceId, input::{Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant}}, funnel}; use crate::engine::{event::{Event, EventType, SourceId, input::{HotKeyEvent, Key, KeyboardEvent, MouseButton, MouseEvent, Status, Variant}}, funnel};
pub struct DetectSource { pub struct DetectSource {
pub receiver: Receiver<(InputEvent, SourceId)>, pub receiver: Receiver<(InputEvent, SourceId)>,
@ -52,7 +52,12 @@ impl<'a> funnel::Source<'a> for DetectSource {
button: mouse_event.button.into(), button: mouse_event.button.into(),
}), }),
}, },
InputEvent::HotKey(_) => todo!(), // TODO InputEvent::HotKey(hotkey_event) => Event {
source_id,
etype: EventType::HotKey(HotKeyEvent {
hotkey_id: hotkey_event.hotkey_id,
})
}
} }
} }
} }

View File

@ -91,7 +91,7 @@ pub fn initialize_and_spawn(
super::engine::funnel::init_and_spawn(SourceCreationOptions { super::engine::funnel::init_and_spawn(SourceCreationOptions {
use_evdev: use_evdev_backend, use_evdev: use_evdev_backend,
evdev_keyboard_rmlvo: keyboard_layout_util::generate_detect_rmlvo(&*config_manager.default()), evdev_keyboard_rmlvo: keyboard_layout_util::generate_detect_rmlvo(&*config_manager.default()),
..Default::default() hotkeys: match_converter.get_hotkeys(),
}) })
.expect("failed to initialize detector module"); .expect("failed to initialize detector module");
let exit_source = super::engine::funnel::exit::ExitSource::new(exit_signal, &sequencer); let exit_source = super::engine::funnel::exit::ExitSource::new(exit_signal, &sequencer);

View File

@ -24,10 +24,12 @@ use espanso_config::{
MatchCause, MatchCause,
}, },
}; };
use espanso_detect::hotkey::HotKey;
use espanso_match::{ use espanso_match::{
regex::RegexMatch, regex::RegexMatch,
rolling::{RollingMatch, StringMatchOptions}, rolling::{RollingMatch, StringMatchOptions},
}; };
use log::error;
use std::iter::FromIterator; use std::iter::FromIterator;
use crate::cli::worker::builtin::BuiltInMatch; use crate::cli::worker::builtin::BuiltInMatch;
@ -79,7 +81,7 @@ impl<'a> MatchConverter<'a> {
matches.push(RollingMatch::from_string( matches.push(RollingMatch::from_string(
m.id, m.id,
&trigger, &trigger,
&StringMatchOptions::default() &StringMatchOptions::default(),
)) ))
} }
} }
@ -101,6 +103,26 @@ impl<'a> MatchConverter<'a> {
matches matches
} }
pub fn get_hotkeys(&self) -> Vec<HotKey> {
let mut hotkeys = Vec::new();
// TODO: read user-defined matches
// Then convert built-in ones
for m in self.builtin_matches {
if let Some(hotkey) = &m.hotkey {
match HotKey::new(m.id, hotkey) {
Ok(hotkey) => hotkeys.push(hotkey),
Err(err) => {
error!("unable to register hotkey: {}, with error: {}", hotkey, err);
}
}
}
}
hotkeys
}
fn global_match_set(&self) -> MatchSet { fn global_match_set(&self) -> MatchSet {
let paths = self.config_store.get_all_match_paths(); let paths = self.config_store.get_all_match_paths();
self.match_store.query(&Vec::from_iter(paths.into_iter())) self.match_store.query(&Vec::from_iter(paths.into_iter()))

View File

@ -116,3 +116,8 @@ pub enum Key {
pub struct ContextMenuClickedEvent { pub struct ContextMenuClickedEvent {
pub context_item_id: u32, pub context_item_id: u32,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct HotKeyEvent {
pub hotkey_id: i32,
}

View File

@ -24,7 +24,7 @@ pub struct MatchesDetectedEvent {
pub matches: Vec<DetectedMatch>, pub matches: Vec<DetectedMatch>,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Default)]
pub struct DetectedMatch { pub struct DetectedMatch {
pub id: i32, pub id: i32,
pub trigger: Option<String>, pub trigger: Option<String>,

View File

@ -55,7 +55,7 @@ pub enum EventType {
// Inputs // Inputs
Keyboard(input::KeyboardEvent), Keyboard(input::KeyboardEvent),
Mouse(input::MouseEvent), Mouse(input::MouseEvent),
// TODO: hotkeys HotKey(input::HotKeyEvent),
TrayIconClicked, TrayIconClicked,
ContextMenuClicked(input::ContextMenuClickedEvent), ContextMenuClicked(input::ContextMenuClickedEvent),

View File

@ -25,7 +25,7 @@ use super::{DisableOptions, MatchFilter, MatchInfoProvider, MatchProvider, Match
delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider}, markdown::MarkdownMiddleware, delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider}, markdown::MarkdownMiddleware,
past_discard::PastEventsDiscardMiddleware, past_discard::PastEventsDiscardMiddleware,
}}; }};
use crate::engine::{event::{Event, EventType}, process::middleware::{context_menu::ContextMenuMiddleware, disable::DisableMiddleware, exit::ExitMiddleware, icon_status::IconStatusMiddleware, image_resolve::ImageResolverMiddleware, search::SearchMiddleware}}; use crate::engine::{event::{Event, EventType}, process::middleware::{context_menu::ContextMenuMiddleware, disable::DisableMiddleware, exit::ExitMiddleware, hotkey::HotKeyMiddleware, icon_status::IconStatusMiddleware, image_resolve::ImageResolverMiddleware, search::SearchMiddleware}};
use std::collections::VecDeque; use std::collections::VecDeque;
pub struct DefaultProcessor<'a> { pub struct DefaultProcessor<'a> {
@ -56,6 +56,7 @@ impl<'a> DefaultProcessor<'a> {
Box::new(IconStatusMiddleware::new()), Box::new(IconStatusMiddleware::new()),
Box::new(MatcherMiddleware::new(matchers, matcher_options_provider)), Box::new(MatcherMiddleware::new(matchers, matcher_options_provider)),
Box::new(ContextMenuMiddleware::new()), Box::new(ContextMenuMiddleware::new()),
Box::new(HotKeyMiddleware::new()),
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)), Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
Box::new(CauseCompensateMiddleware::new()), Box::new(CauseCompensateMiddleware::new()),
Box::new(MultiplexMiddleware::new(multiplexer)), Box::new(MultiplexMiddleware::new(multiplexer)),

View File

@ -30,7 +30,7 @@ impl CauseCompensateMiddleware {
impl Middleware for CauseCompensateMiddleware { impl Middleware for CauseCompensateMiddleware {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"discard" "cause_compensate"
} }
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {

View File

@ -0,0 +1,52 @@
/*
* This file is part of espanso id: (), trigger: (), trigger: (), left_separator: (), right_separator: (), args: () left_separator: (), right_separator: (), args: () id: (), trigger: (), left_separator: (), right_separator: (), args: ().
*
* Copyright (C) 2019-2021 Federico Terzi
*
* espanso is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* espanso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use super::super::Middleware;
use crate::engine::{event::{Event, EventType, internal::{DetectedMatch, MatchesDetectedEvent}}};
pub struct HotKeyMiddleware {}
impl HotKeyMiddleware {
pub fn new() -> Self {
Self {}
}
}
impl Middleware for HotKeyMiddleware {
fn name(&self) -> &'static str {
"hotkey"
}
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
if let EventType::HotKey(m_event) = &event.etype {
return Event::caused_by(event.source_id, EventType::MatchesDetected(MatchesDetectedEvent {
matches: vec![
DetectedMatch {
id: m_event.hotkey_id,
..Default::default()
}
],
}));
}
event
}
}
// TODO: test

View File

@ -24,6 +24,7 @@ pub mod cursor_hint;
pub mod delay_modifiers; pub mod delay_modifiers;
pub mod disable; pub mod disable;
pub mod exit; pub mod exit;
pub mod hotkey;
pub mod icon_status; pub mod icon_status;
pub mod image_resolve; pub mod image_resolve;
pub mod match_select; pub mod match_select;

View File

@ -78,6 +78,10 @@ macro_rules! generate_patchable_config {
fn search_trigger(&self) -> Option<String> { fn search_trigger(&self) -> Option<String> {
self.base.search_trigger() self.base.search_trigger()
} }
fn search_shortcut(&self) -> Option<String> {
self.base.search_shortcut()
}
} }
}; };
} }