fix: custom hotkey

This commit is contained in:
PandaDEV 2024-11-15 23:03:46 +10:00
parent 32a43c6c2d
commit b545f2e0fb
No known key found for this signature in database
GPG key ID: 13EFF9BAF70EE75C
2 changed files with 103 additions and 66 deletions

View file

@ -1,59 +1,78 @@
use crate::api::database::get_keybind;
use crate::utils::commands::center_window_on_current_monitor; use crate::utils::commands::center_window_on_current_monitor;
use global_hotkey::{ use global_hotkey::{
hotkey::{Code, HotKey, Modifiers}, hotkey::{Code, HotKey, Modifiers},
GlobalHotKeyEvent, GlobalHotKeyManager, GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState,
}; };
use std::str::FromStr; use std::str::FromStr;
use tauri::{AppHandle, Listener, Manager}; use tauri::{AppHandle, Listener, Manager};
use std::sync::Arc;
pub fn setup(app_handle: tauri::AppHandle) { pub fn setup(app_handle: tauri::AppHandle) {
let app_handle_clone = app_handle.clone(); let app_handle_clone = app_handle.clone();
tauri::async_runtime::spawn(async move { let manager = Arc::new(GlobalHotKeyManager::new().expect("Failed to initialize hotkey manager"));
match get_keybind(app_handle_clone.clone()).await { let manager_clone = manager.clone();
Ok(keybind) => { let manager_clone2 = manager.clone();
if !keybind.is_empty() {
let keybind_str = keybind.join("+"); let rt = app_handle.state::<tokio::runtime::Runtime>();
println!("Keybind: {:?}", keybind_str); let initial_keybind = rt.block_on(crate::api::database::get_keybind(app_handle_clone.clone()))
if let Err(e) = register_shortcut(&app_handle_clone, &keybind_str) { .expect("Failed to get initial keybind");
eprintln!("Error registering shortcut: {:?}", e); let initial_shortcut = initial_keybind.join("+");
}
} let initial_shortcut_clone = initial_shortcut.clone();
}
Err(e) => { if let Err(e) = register_shortcut(&manager, &initial_shortcut) {
eprintln!("Error getting keybind: {:?}", e); eprintln!("Error registering initial shortcut: {:?}", e);
} }
app_handle.listen("update-shortcut", move |event| {
let payload_str = event.payload().to_string();
if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_clone) {
let _ = manager_clone.unregister(old_hotkey);
}
if let Err(e) = register_shortcut(&manager_clone, &payload_str) {
eprintln!("Error re-registering shortcut: {:?}", e);
} }
}); });
let app_handle_for_listener = app_handle.clone(); app_handle.listen("save_keybind", move |event| {
app_handle.listen("update-shortcut", move |event| {
let payload_str = event.payload().to_string(); let payload_str = event.payload().to_string();
if let Err(e) = register_shortcut(&app_handle_for_listener, &payload_str) {
eprintln!("Error re-registering shortcut: {:?}", e); if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut) {
let _ = manager_clone2.unregister(old_hotkey);
}
if let Err(e) = register_shortcut(&manager_clone2, &payload_str) {
eprintln!("Error registering saved shortcut: {:?}", e);
} }
}); });
let app_handle_for_hotkey = app_handle.clone(); let app_handle_for_hotkey = app_handle.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
loop { loop {
if let Ok(_) = GlobalHotKeyEvent::receiver().recv() { match GlobalHotKeyEvent::receiver().recv() {
handle_hotkey_event(&app_handle_for_hotkey); Ok(event) => {
if event.state == HotKeyState::Released {
continue;
}
handle_hotkey_event(&app_handle_for_hotkey);
}
Err(e) => {
eprintln!("Error receiving hotkey event: {:?}", e);
}
} }
} }
}); });
} }
fn register_shortcut( fn register_shortcut(
_app_handle: &tauri::AppHandle, manager: &Arc<GlobalHotKeyManager>,
shortcut: &str, shortcut: &str,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let manager = GlobalHotKeyManager::new()?;
let hotkey = parse_hotkey(shortcut)?; let hotkey = parse_hotkey(shortcut)?;
manager.register(hotkey)?; manager.register(hotkey)?;
println!("Listening for keybind: {}", shortcut);
Ok(()) Ok(())
} }
@ -61,35 +80,32 @@ fn parse_hotkey(shortcut: &str) -> Result<HotKey, Box<dyn std::error::Error>> {
let mut modifiers = Modifiers::empty(); let mut modifiers = Modifiers::empty();
let mut code = None; let mut code = None;
for part in shortcut.split('+') { let shortcut = shortcut.replace("\"", "");
let part = part;
if part.to_lowercase().starts_with("ctrl") || part.to_lowercase().starts_with("control") {
modifiers |= Modifiers::CONTROL;
} else if part.to_lowercase().starts_with("alt") {
modifiers |= Modifiers::ALT;
} else if part.to_lowercase().starts_with("shift") {
modifiers |= Modifiers::SHIFT;
} else if part.to_lowercase().starts_with("super") || part.to_lowercase().starts_with("meta") || part.to_lowercase().starts_with("cmd") {
modifiers |= Modifiers::META; for part in shortcut.split('+') {
} else { let part = part.trim().to_lowercase();
let pascal_case_key = part match part.as_str() {
.split(|c: char| !c.is_alphanumeric()) "ctrl" | "control" | "controlleft" => modifiers |= Modifiers::CONTROL,
.map(|word| { "alt" | "altleft" | "optionleft" => modifiers |= Modifiers::ALT,
let mut chars = word.chars(); "shift" | "shiftleft" => modifiers |= Modifiers::SHIFT,
let first_char = chars.next().unwrap().to_uppercase().collect::<String>(); "super" | "meta" | "cmd" | "metaleft" => modifiers |= Modifiers::META,
let rest = chars.as_str(); key => {
first_char + rest let key_code = if key.starts_with("key") {
}) "Key".to_string() + &key[3..].to_uppercase()
.collect::<String>(); } else if key.len() == 1 && key.chars().next().unwrap().is_alphabetic() {
code = Some( "Key".to_string() + &key.to_uppercase()
Code::from_str(&pascal_case_key) } else {
.map_err(|_| format!("Invalid key: {}", pascal_case_key))?, key.to_string()
); };
code = Some(Code::from_str(&key_code)
.map_err(|_| format!("Invalid key code: {}", key_code))?);
}
} }
} }
Ok(HotKey::new(Some(modifiers), code.unwrap())) let key_code = code.ok_or_else(|| format!("No valid key code found in shortcut: {}", shortcut))?;
Ok(HotKey::new(Some(modifiers), key_code))
} }
fn handle_hotkey_event(app_handle: &AppHandle) { fn handle_hotkey_event(app_handle: &AppHandle) {
@ -97,8 +113,16 @@ fn handle_hotkey_event(app_handle: &AppHandle) {
if window.is_visible().unwrap() { if window.is_visible().unwrap() {
window.hide().unwrap(); window.hide().unwrap();
} else { } else {
window.set_always_on_top(true).unwrap();
window.show().unwrap(); window.show().unwrap();
window.set_focus().unwrap(); window.set_focus().unwrap();
let window_clone = window.clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(100));
window_clone.set_always_on_top(false).unwrap();
});
center_window_on_current_monitor(&window); center_window_on_current_monitor(&window);
} }
} }

View file

@ -7,11 +7,12 @@ mod api;
mod utils; mod utils;
use tauri::Manager; use tauri::Manager;
use tauri::WebviewUrl;
use tauri::WebviewWindow;
use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_prevent_default::Flags; use tauri_plugin_prevent_default::Flags;
fn main() { fn main() {
#[allow(unused_variables)]
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_clipboard::init()) .plugin(tauri_plugin_clipboard::init())
.plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_os::init())
@ -31,23 +32,35 @@ fn main() {
.setup(|app| { .setup(|app| {
let app_handle = app.handle().clone(); let app_handle = app.handle().clone();
let main_window = if let Some(window) = app.get_webview_window("main") {
window
} else {
WebviewWindow::builder(
app.handle(),
"main",
WebviewUrl::App("index.html".into())
)
.title("Qopy")
.resizable(false)
.fullscreen(false)
.inner_size(750.0, 474.0)
.focused(true)
.skip_taskbar(true)
.visible(false)
.decorations(false)
.transparent(true)
.always_on_top(false)
.build()?
};
let _ = api::database::setup(app); let _ = api::database::setup(app);
api::hotkeys::setup(app_handle.clone()); api::hotkeys::setup(app_handle.clone());
api::tray::setup(app)?; api::tray::setup(app)?;
api::clipboard::setup(app.handle()); api::clipboard::setup(app.handle());
let _ = api::clipboard::start_monitor(app_handle.clone()); let _ = api::clipboard::start_monitor(app_handle.clone());
if let Some(window) = app.get_webview_window("main") { utils::commands::center_window_on_current_monitor(&main_window);
utils::commands::center_window_on_current_monitor(&window); main_window.hide()?;
window.hide().unwrap();
}
// #[cfg(dev)]
// {
// let window = app.get_webview_window("main").unwrap();
// window.open_devtools();
// window.close_devtools();
// }
let app_data_dir = app let app_data_dir = app
.path() .path()
@ -61,10 +74,10 @@ fn main() {
Ok(()) Ok(())
}) })
.on_window_event(|app, event| { .on_window_event(|_app, _event| {
#[cfg(not(dev))] #[cfg(not(dev))]
if let tauri::WindowEvent::Focused(false) = event { if let tauri::WindowEvent::Focused(false) = _event {
if let Some(window) = app.get_webview_window("main") { if let Some(window) = _app.get_webview_window("main") {
let _ = window.hide(); let _ = window.hide();
} }
} }