-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
103 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,104 +1,128 @@ | ||
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::<tokio::runtime::Runtime>(); | ||
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); | ||
} | ||
|
||
let app_handle_for_listener = app_handle.clone(); | ||
app_handle.listen("update-shortcut", move |event| { | ||
let payload_str = event.payload().to_string(); | ||
if let Err(e) = register_shortcut(&app_handle_for_listener, &payload_str) { | ||
|
||
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); | ||
} | ||
}); | ||
|
||
app_handle.listen("save_keybind", move |event| { | ||
let payload_str = event.payload().to_string(); | ||
|
||
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<GlobalHotKeyManager>, | ||
shortcut: &str, | ||
) -> Result<(), Box<dyn std::error::Error>> { | ||
let manager = GlobalHotKeyManager::new()?; | ||
let hotkey = parse_hotkey(shortcut)?; | ||
manager.register(hotkey)?; | ||
|
||
println!("Listening for keybind: {}", shortcut); | ||
Ok(()) | ||
} | ||
|
||
fn parse_hotkey(shortcut: &str) -> Result<HotKey, Box<dyn std::error::Error>> { | ||
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::<String>(); | ||
let rest = chars.as_str(); | ||
first_char + rest | ||
}) | ||
.collect::<String>(); | ||
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) { | ||
let window = app_handle.get_webview_window("main").unwrap(); | ||
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters