From b545f2e0fb48e22c5f2cb740044c70cf47584f51 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Fri, 15 Nov 2024 23:03:46 +1000 Subject: [PATCH] fix: custom hotkey --- src-tauri/src/api/hotkeys.rs | 126 +++++++++++++++++++++-------------- src-tauri/src/main.rs | 43 +++++++----- 2 files changed, 103 insertions(+), 66 deletions(-) diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index 94704ae..3a117e2 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -1,59 +1,78 @@ -use crate::api::database::get_keybind; use crate::utils::commands::center_window_on_current_monitor; use global_hotkey::{ hotkey::{Code, HotKey, Modifiers}, - GlobalHotKeyEvent, GlobalHotKeyManager, + GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState, }; use std::str::FromStr; use tauri::{AppHandle, Listener, Manager}; +use std::sync::Arc; pub fn setup(app_handle: tauri::AppHandle) { let app_handle_clone = app_handle.clone(); - tauri::async_runtime::spawn(async move { - match get_keybind(app_handle_clone.clone()).await { - Ok(keybind) => { - if !keybind.is_empty() { - let keybind_str = keybind.join("+"); - println!("Keybind: {:?}", keybind_str); - if let Err(e) = register_shortcut(&app_handle_clone, &keybind_str) { - eprintln!("Error registering shortcut: {:?}", e); - } - } - } - Err(e) => { - eprintln!("Error getting keybind: {:?}", e); - } + let manager = Arc::new(GlobalHotKeyManager::new().expect("Failed to initialize hotkey manager")); + let manager_clone = manager.clone(); + let manager_clone2 = manager.clone(); + + let rt = app_handle.state::(); + let initial_keybind = rt.block_on(crate::api::database::get_keybind(app_handle_clone.clone())) + .expect("Failed to get initial keybind"); + let initial_shortcut = initial_keybind.join("+"); + + let initial_shortcut_clone = initial_shortcut.clone(); + + if let Err(e) = register_shortcut(&manager, &initial_shortcut) { + 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("update-shortcut", move |event| { + app_handle.listen("save_keybind", move |event| { 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(); tauri::async_runtime::spawn(async move { loop { - if let Ok(_) = GlobalHotKeyEvent::receiver().recv() { - handle_hotkey_event(&app_handle_for_hotkey); + match GlobalHotKeyEvent::receiver().recv() { + 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( - _app_handle: &tauri::AppHandle, + manager: &Arc, shortcut: &str, ) -> Result<(), Box> { - let manager = GlobalHotKeyManager::new()?; let hotkey = parse_hotkey(shortcut)?; manager.register(hotkey)?; - - println!("Listening for keybind: {}", shortcut); Ok(()) } @@ -61,35 +80,32 @@ fn parse_hotkey(shortcut: &str) -> Result> { let mut modifiers = Modifiers::empty(); let mut code = None; + let shortcut = shortcut.replace("\"", ""); + for part in shortcut.split('+') { - 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; - } else { - let pascal_case_key = part - .split(|c: char| !c.is_alphanumeric()) - .map(|word| { - let mut chars = word.chars(); - let first_char = chars.next().unwrap().to_uppercase().collect::(); - let rest = chars.as_str(); - first_char + rest - }) - .collect::(); - code = Some( - Code::from_str(&pascal_case_key) - .map_err(|_| format!("Invalid key: {}", pascal_case_key))?, - ); + let part = part.trim().to_lowercase(); + match part.as_str() { + "ctrl" | "control" | "controlleft" => modifiers |= Modifiers::CONTROL, + "alt" | "altleft" | "optionleft" => modifiers |= Modifiers::ALT, + "shift" | "shiftleft" => modifiers |= Modifiers::SHIFT, + "super" | "meta" | "cmd" | "metaleft" => modifiers |= Modifiers::META, + key => { + let key_code = if key.starts_with("key") { + "Key".to_string() + &key[3..].to_uppercase() + } else if key.len() == 1 && key.chars().next().unwrap().is_alphabetic() { + "Key".to_string() + &key.to_uppercase() + } else { + 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) { @@ -97,8 +113,16 @@ fn handle_hotkey_event(app_handle: &AppHandle) { if window.is_visible().unwrap() { window.hide().unwrap(); } else { + window.set_always_on_top(true).unwrap(); window.show().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); } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 10c61f3..ac84c7b 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,11 +7,12 @@ mod api; mod utils; use tauri::Manager; +use tauri::WebviewUrl; +use tauri::WebviewWindow; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_prevent_default::Flags; fn main() { - #[allow(unused_variables)] tauri::Builder::default() .plugin(tauri_plugin_clipboard::init()) .plugin(tauri_plugin_os::init()) @@ -31,23 +32,35 @@ fn main() { .setup(|app| { 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); api::hotkeys::setup(app_handle.clone()); api::tray::setup(app)?; api::clipboard::setup(app.handle()); let _ = api::clipboard::start_monitor(app_handle.clone()); - if let Some(window) = app.get_webview_window("main") { - utils::commands::center_window_on_current_monitor(&window); - window.hide().unwrap(); - } - - // #[cfg(dev)] - // { - // let window = app.get_webview_window("main").unwrap(); - // window.open_devtools(); - // window.close_devtools(); - // } + utils::commands::center_window_on_current_monitor(&main_window); + main_window.hide()?; let app_data_dir = app .path() @@ -61,10 +74,10 @@ fn main() { Ok(()) }) - .on_window_event(|app, event| { + .on_window_event(|_app, _event| { #[cfg(not(dev))] - if let tauri::WindowEvent::Focused(false) = event { - if let Some(window) = app.get_webview_window("main") { + if let tauri::WindowEvent::Focused(false) = _event { + if let Some(window) = _app.get_webview_window("main") { let _ = window.hide(); } }