feat(core): implement hotkey detection for builtin matches and wire up search
This commit is contained in:
parent
fa2709d43b
commit
ba4f9400ea
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()))
|
||||||
|
|
|
@ -115,4 +115,9 @@ pub enum Key {
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
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,
|
||||||
}
|
}
|
|
@ -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>,
|
||||||
|
|
|
@ -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),
|
||||||
|
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
52
espanso/src/engine/process/middleware/hotkey.rs
Normal file
52
espanso/src/engine/process/middleware/hotkey.rs
Normal 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
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user