mirror of
https://github.com/0PandaDEV/Qopy.git
synced 2025-04-21 13:14:04 +02:00
refactor: improve keybind saving logic and error handling
This commit is contained in:
parent
6656af8ab1
commit
0c28a5b0db
8 changed files with 478 additions and 154 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Get Started
|
# Get Started
|
||||||
|
|
||||||
The default hotkey for Qopy is Windows+V which is also the hotkey for the default clipboard manager to turn that off follow [this guide](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md#disable-windowsv-for-default-clipboard-manager).
|
The default hotkey for Qopy is Windows+V which is also the hotkey for the default clipboard manager to turn that off follow [this guide](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md#disable-windowsv-for-default-clipboard-manager).
|
||||||
|
|
||||||
All the data of Qopy is stored inside of a SQLite database.
|
All the data of Qopy is stored inside of a SQLite database.
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ All the data of Qopy is stored inside of a SQLite database.
|
||||||
|
|
||||||
## Disable Windows+V for default clipboard manager
|
## Disable Windows+V for default clipboard manager
|
||||||
|
|
||||||
https://github.com/user-attachments/assets/723f9e07-3190-46ec-9bb7-15dfc112f620
|
<video src="https://github.com/user-attachments/assets/723f9e07-3190-46ec-9bb7-15dfc112f620" controls title="Disable Windows+V for default clipboard manager"></video>
|
||||||
|
|
||||||
To disable the default clipboard manager popup from windows open Command prompt and run this command
|
To disable the default clipboard manager popup from windows open Command prompt and run this command
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
<div
|
<div
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
@keydown="onKeyDown"
|
|
||||||
class="keybind-input"
|
class="keybind-input"
|
||||||
ref="keybindInput"
|
ref="keybindInput"
|
||||||
tabindex="0">
|
tabindex="0">
|
||||||
|
@ -44,7 +43,7 @@
|
||||||
class="key"
|
class="key"
|
||||||
:class="{ modifier: isModifier(key) }"
|
:class="{ modifier: isModifier(key) }"
|
||||||
v-for="(key, index) in keybind">
|
v-for="(key, index) in keybind">
|
||||||
{{ keyToDisplay(key) }}
|
{{ keyToLabel(key) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,10 +57,11 @@ import { onMounted, onUnmounted, reactive, ref } from "vue";
|
||||||
import { platform } from "@tauri-apps/plugin-os";
|
import { platform } from "@tauri-apps/plugin-os";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { Key } from "wrdu-keyboard/key";
|
import { Key } from "wrdu-keyboard/key";
|
||||||
|
import { KeyValues, KeyLabels } from "../types/keys";
|
||||||
|
|
||||||
const activeModifiers = reactive<Set<string>>(new Set());
|
const activeModifiers = reactive<Set<KeyValues>>(new Set());
|
||||||
const isKeybindInputFocused = ref(false);
|
const isKeybindInputFocused = ref(false);
|
||||||
const keybind = ref<string[]>([]);
|
const keybind = ref<KeyValues[]>([]);
|
||||||
const keybindInput = ref<HTMLElement | null>(null);
|
const keybindInput = ref<HTMLElement | null>(null);
|
||||||
const lastBlurTime = ref(0);
|
const lastBlurTime = ref(0);
|
||||||
const os = ref("");
|
const os = ref("");
|
||||||
|
@ -69,52 +69,27 @@ const router = useRouter();
|
||||||
const keyboard = useKeyboard();
|
const keyboard = useKeyboard();
|
||||||
const showEmptyKeybindError = ref(false);
|
const showEmptyKeybindError = ref(false);
|
||||||
|
|
||||||
const keyToDisplayMap: Record<string, string> = {
|
|
||||||
" ": "Space",
|
|
||||||
Alt: "Alt",
|
|
||||||
AltLeft: "Alt L",
|
|
||||||
AltRight: "Alt R",
|
|
||||||
ArrowDown: "↓",
|
|
||||||
ArrowLeft: "←",
|
|
||||||
ArrowRight: "→",
|
|
||||||
ArrowUp: "↑",
|
|
||||||
Control: "Ctrl",
|
|
||||||
ControlLeft: "Ctrl L",
|
|
||||||
ControlRight: "Ctrl R",
|
|
||||||
Enter: "↵",
|
|
||||||
Meta: "Meta",
|
|
||||||
MetaLeft: "Meta L",
|
|
||||||
MetaRight: "Meta R",
|
|
||||||
Shift: "⇧",
|
|
||||||
ShiftLeft: "⇧ L",
|
|
||||||
ShiftRight: "⇧ R",
|
|
||||||
};
|
|
||||||
|
|
||||||
const modifierKeySet = new Set([
|
const modifierKeySet = new Set([
|
||||||
"Alt",
|
KeyValues.AltLeft,
|
||||||
"AltLeft",
|
KeyValues.AltRight,
|
||||||
"AltRight",
|
KeyValues.ControlLeft,
|
||||||
"Control",
|
KeyValues.ControlRight,
|
||||||
"ControlLeft",
|
KeyValues.MetaLeft,
|
||||||
"ControlRight",
|
KeyValues.MetaRight,
|
||||||
"Meta",
|
KeyValues.ShiftLeft,
|
||||||
"MetaLeft",
|
KeyValues.ShiftRight,
|
||||||
"MetaRight",
|
|
||||||
"Shift",
|
|
||||||
"ShiftLeft",
|
|
||||||
"ShiftRight",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isModifier = (key: string): boolean => {
|
const isModifier = (key: KeyValues): boolean => {
|
||||||
return modifierKeySet.has(key);
|
return modifierKeySet.has(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
const keyToDisplay = (key: string): string => {
|
const keyToLabel = (key: KeyValues): string => {
|
||||||
return keyToDisplayMap[key] || key;
|
return KeyLabels[key] || key;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateKeybind = () => {
|
const updateKeybind = () => {
|
||||||
const modifiers = Array.from(activeModifiers).sort();
|
const modifiers = Array.from(activeModifiers);
|
||||||
const nonModifiers = keybind.value.filter((key) => !isModifier(key));
|
const nonModifiers = keybind.value.filter((key) => !isModifier(key));
|
||||||
keybind.value = [...modifiers, ...nonModifiers];
|
keybind.value = [...modifiers, ...nonModifiers];
|
||||||
};
|
};
|
||||||
|
@ -133,9 +108,9 @@ const onFocus = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
const key = event.code;
|
const key = event.code as KeyValues;
|
||||||
|
|
||||||
if (key === "Escape") {
|
if (key === KeyValues.Escape) {
|
||||||
if (keybindInput.value) {
|
if (keybindInput.value) {
|
||||||
keybindInput.value.blur();
|
keybindInput.value.blur();
|
||||||
}
|
}
|
||||||
|
@ -155,8 +130,7 @@ const onKeyDown = (event: KeyboardEvent) => {
|
||||||
|
|
||||||
const saveKeybind = async () => {
|
const saveKeybind = async () => {
|
||||||
if (keybind.value.length > 0) {
|
if (keybind.value.length > 0) {
|
||||||
console.log("Saving keybind", keybind.value);
|
await invoke("save_keybind", { keybind: keybind.value });
|
||||||
await invoke("save_keybind", { keybind: keybind });
|
|
||||||
} else {
|
} else {
|
||||||
showEmptyKeybindError.value = true;
|
showEmptyKeybindError.value = true;
|
||||||
}
|
}
|
||||||
|
@ -165,8 +139,13 @@ const saveKeybind = async () => {
|
||||||
os.value = platform();
|
os.value = platform();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
keyboard.down([Key.All], (event) => {
|
||||||
|
if (isKeybindInputFocused.value) {
|
||||||
|
onKeyDown(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
keyboard.down([Key.Escape], (event) => {
|
keyboard.down([Key.Escape], (event) => {
|
||||||
console.log("Escape key pressed");
|
|
||||||
if (isKeybindInputFocused.value) {
|
if (isKeybindInputFocused.value) {
|
||||||
keybindInput.value?.blur();
|
keybindInput.value?.blur();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
diff --git a/node_modules/wrdu-keyboard/.DS_Store b/.DS_Store
|
diff --git a/node_modules/wrdu-keyboard/.DS_Store b/.DS_Store
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..fabbd951c2d14c46fd10fa167b8836d116bc0db6
|
index 0000000000000000000000000000000000000000..4b7e9446f3580fab3e4feaba097bcdaf98c5833c
|
||||||
Binary files /dev/null and b/.DS_Store differ
|
Binary files /dev/null and b/.DS_Store differ
|
||||||
diff --git a/dist/runtime/keyboard.d.ts b/dist/runtime/keyboard.d.ts
|
diff --git a/dist/runtime/keyboard.d.ts b/dist/runtime/keyboard.d.ts
|
||||||
index aeae40f3d2bc3efd459cce04c29c21c43884154d..6131bab4895ebb3048a5225f366430d23c5f1f13 100644
|
index aeae40f3d2bc3efd459cce04c29c21c43884154d..6131bab4895ebb3048a5225f366430d23c5f1f13 100644
|
||||||
|
@ -27,10 +27,10 @@ index aeae40f3d2bc3efd459cce04c29c21c43884154d..6131bab4895ebb3048a5225f366430d2
|
||||||
up: New;
|
up: New;
|
||||||
prevent: {
|
prevent: {
|
||||||
diff --git a/dist/runtime/keyboard.js b/dist/runtime/keyboard.js
|
diff --git a/dist/runtime/keyboard.js b/dist/runtime/keyboard.js
|
||||||
index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cdb37820aa 100644
|
index e16f600258cee90d185ffc52777bed95c14bd93e..5ddec447a5dc66ffe063eb9f9dd765c9045bdaf7 100644
|
||||||
--- a/dist/runtime/keyboard.js
|
--- a/dist/runtime/keyboard.js
|
||||||
+++ b/dist/runtime/keyboard.js
|
+++ b/dist/runtime/keyboard.js
|
||||||
@@ -1,13 +1,14 @@
|
@@ -1,45 +1,54 @@
|
||||||
import { Key } from "./types/keys.js";
|
import { Key } from "./types/keys.js";
|
||||||
import { defineNuxtPlugin } from "#app";
|
import { defineNuxtPlugin } from "#app";
|
||||||
-const getKeyString = (keys) => keys[0] == Key.All ? keys.sort().join("+") : "All";
|
-const getKeyString = (keys) => keys[0] == Key.All ? keys.sort().join("+") : "All";
|
||||||
|
@ -45,18 +45,31 @@ index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cd
|
||||||
+ const key = event.code;
|
+ const key = event.code;
|
||||||
+ pressedKeys.add(key);
|
+ pressedKeys.add(key);
|
||||||
const pressedArray = Array.from(pressedKeys);
|
const pressedArray = Array.from(pressedKeys);
|
||||||
const keyString = getKeyString(pressedArray);
|
- const keyString = getKeyString(pressedArray);
|
||||||
if (handlers.down[keyString]) {
|
- if (handlers.down[keyString]) {
|
||||||
@@ -17,13 +18,16 @@ const onKeydown = (event) => {
|
- handlers.down[keyString].forEach((eventHandler) => {
|
||||||
}
|
- if (eventHandler.prevent) {
|
||||||
eventHandler.handler(event);
|
- event.preventDefault();
|
||||||
if (eventHandler.once) {
|
- }
|
||||||
|
- eventHandler.handler(event);
|
||||||
|
- if (eventHandler.once) {
|
||||||
- handlers.down[keyString] = handlers.down[keyString].filter((h) => h !== eventHandler);
|
- handlers.down[keyString] = handlers.down[keyString].filter((h) => h !== eventHandler);
|
||||||
+ handlers.down[keyString] = handlers.down[keyString].filter(
|
- }
|
||||||
+ (h) => h !== eventHandler
|
- });
|
||||||
+ );
|
+ for (const keyString of [getKeyString(pressedArray), "All"]) {
|
||||||
}
|
+ if (handlers.down[keyString]) {
|
||||||
});
|
+ handlers.down[keyString].forEach((eventHandler) => {
|
||||||
|
+ if (eventHandler.prevent) {
|
||||||
|
+ event.preventDefault();
|
||||||
|
+ }
|
||||||
|
+ eventHandler.handler(event);
|
||||||
|
+ if (eventHandler.once) {
|
||||||
|
+ handlers.down[keyString] = handlers.down[keyString].filter(
|
||||||
|
+ (h) => h !== eventHandler
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onKeyup = (event) => {
|
const onKeyup = (event) => {
|
||||||
|
@ -64,18 +77,31 @@ index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cd
|
||||||
+ const key = event.code;
|
+ const key = event.code;
|
||||||
+ pressedKeys.delete(key);
|
+ pressedKeys.delete(key);
|
||||||
const releasedArray = Array.from(pressedKeys);
|
const releasedArray = Array.from(pressedKeys);
|
||||||
const keyString = getKeyString(releasedArray);
|
- const keyString = getKeyString(releasedArray);
|
||||||
if (handlers.up[keyString]) {
|
- if (handlers.up[keyString]) {
|
||||||
@@ -33,13 +37,16 @@ const onKeyup = (event) => {
|
- handlers.up[keyString].forEach((eventHandler) => {
|
||||||
}
|
- if (eventHandler.prevent) {
|
||||||
eventHandler.handler(event);
|
- event.preventDefault();
|
||||||
if (eventHandler.once) {
|
- }
|
||||||
|
- eventHandler.handler(event);
|
||||||
|
- if (eventHandler.once) {
|
||||||
- handlers.up[keyString] = handlers.up[keyString].filter((h) => h !== eventHandler);
|
- handlers.up[keyString] = handlers.up[keyString].filter((h) => h !== eventHandler);
|
||||||
+ handlers.up[keyString] = handlers.up[keyString].filter(
|
- }
|
||||||
+ (h) => h !== eventHandler
|
- });
|
||||||
+ );
|
+ for (const keyString of [getKeyString(releasedArray), "All"]) {
|
||||||
}
|
+ if (handlers.up[keyString]) {
|
||||||
});
|
+ handlers.up[keyString].forEach((eventHandler) => {
|
||||||
|
+ if (eventHandler.prevent) {
|
||||||
|
+ event.preventDefault();
|
||||||
|
+ }
|
||||||
|
+ eventHandler.handler(event);
|
||||||
|
+ if (eventHandler.once) {
|
||||||
|
+ handlers.up[keyString] = handlers.up[keyString].filter(
|
||||||
|
+ (h) => h !== eventHandler
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const init = () => {
|
const init = () => {
|
||||||
|
@ -84,36 +110,18 @@ index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cd
|
||||||
window.addEventListener("keydown", onKeydown);
|
window.addEventListener("keydown", onKeydown);
|
||||||
window.addEventListener("keyup", onKeyup);
|
window.addEventListener("keyup", onKeyup);
|
||||||
};
|
};
|
||||||
@@ -47,6 +54,20 @@ const stop = () => {
|
@@ -47,6 +56,10 @@ const stop = () => {
|
||||||
window.removeEventListener("keydown", onKeydown);
|
window.removeEventListener("keydown", onKeydown);
|
||||||
window.removeEventListener("keyup", onKeyup);
|
window.removeEventListener("keyup", onKeyup);
|
||||||
};
|
};
|
||||||
+const unregisterAll = () => {
|
+const unregisterAll = () => {
|
||||||
+ Object.keys(handlers.down).forEach((key) => {
|
|
||||||
+ handlers.down[key].forEach((handler) => {
|
|
||||||
+ console.log(`Unregistering ${key} ${handler.handler.toString()}`);
|
|
||||||
+ });
|
|
||||||
+ });
|
|
||||||
+ Object.keys(handlers.up).forEach((key) => {
|
|
||||||
+ handlers.up[key].forEach((handler) => {
|
|
||||||
+ console.log(`Unregistering ${key} ${handler.handler.toString()}`);
|
|
||||||
+ });
|
|
||||||
+ });
|
|
||||||
+ handlers.down = {};
|
+ handlers.down = {};
|
||||||
+ handlers.up = {};
|
+ handlers.up = {};
|
||||||
+};
|
+};
|
||||||
const down = (keys, handler, config = {}) => {
|
const down = (keys, handler, config = {}) => {
|
||||||
if (keys.includes(Key.All)) {
|
if (keys.includes(Key.All)) {
|
||||||
keys = [Key.All];
|
keys = [Key.All];
|
||||||
@@ -59,6 +80,7 @@ const down = (keys, handler, config = {}) => {
|
@@ -84,6 +97,7 @@ const keyboard = defineNuxtPlugin((nuxtApp) => {
|
||||||
handlers.down[key] = [];
|
|
||||||
}
|
|
||||||
const { once = false, prevent = false } = config;
|
|
||||||
+ console.log(key, handler.toString());
|
|
||||||
handlers.down[key].push({ handler, prevent, once });
|
|
||||||
};
|
|
||||||
const up = (keys, handler, config = {}) => {
|
|
||||||
@@ -84,6 +106,7 @@ const keyboard = defineNuxtPlugin((nuxtApp) => {
|
|
||||||
keyboard: {
|
keyboard: {
|
||||||
init,
|
init,
|
||||||
stop,
|
stop,
|
||||||
|
|
|
@ -1,48 +1,61 @@
|
||||||
use tauri_plugin_aptabase::EventTracker;
|
|
||||||
use crate::utils::commands::center_window_on_current_monitor;
|
use crate::utils::commands::center_window_on_current_monitor;
|
||||||
|
use crate::utils::keys::KeyCode;
|
||||||
use global_hotkey::{
|
use global_hotkey::{
|
||||||
hotkey::{Code, HotKey, Modifiers},
|
hotkey::{Code, HotKey, Modifiers},
|
||||||
GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState,
|
GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState,
|
||||||
};
|
};
|
||||||
use std::cell::RefCell;
|
use lazy_static::lazy_static;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::Mutex;
|
||||||
use tauri::{AppHandle, Listener, Manager};
|
use tauri::{AppHandle, Listener, Manager};
|
||||||
|
use tauri_plugin_aptabase::EventTracker;
|
||||||
|
|
||||||
thread_local! {
|
lazy_static! {
|
||||||
static HOTKEY_MANAGER: RefCell<Option<GlobalHotKeyManager>> = RefCell::new(None);
|
static ref HOTKEY_MANAGER: Mutex<Option<GlobalHotKeyManager>> = Mutex::new(None);
|
||||||
|
static ref REGISTERED_HOTKEYS: Mutex<Vec<HotKey>> = Mutex::new(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
let manager = GlobalHotKeyManager::new().expect("Failed to initialize hotkey manager");
|
let manager = match GlobalHotKeyManager::new() {
|
||||||
HOTKEY_MANAGER.with(|m| *m.borrow_mut() = Some(manager));
|
Ok(manager) => manager,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to initialize hotkey manager: {:?}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut manager_guard = HOTKEY_MANAGER.lock().unwrap();
|
||||||
|
*manager_guard = Some(manager);
|
||||||
|
}
|
||||||
|
|
||||||
let rt = app_handle.state::<tokio::runtime::Runtime>();
|
let rt = app_handle.state::<tokio::runtime::Runtime>();
|
||||||
let initial_keybind = rt
|
let initial_keybind = rt
|
||||||
.block_on(crate::db::settings::get_keybind(app_handle_clone.clone()))
|
.block_on(crate::db::settings::get_keybind(app_handle_clone.clone()))
|
||||||
.expect("Failed to get initial keybind");
|
.expect("Failed to get initial keybind");
|
||||||
let initial_shortcut = initial_keybind.join("+");
|
|
||||||
|
|
||||||
let initial_shortcut_for_update = initial_shortcut.clone();
|
let initial_shortcut_for_update = initial_keybind.clone();
|
||||||
let initial_shortcut_for_save = initial_shortcut.clone();
|
let initial_shortcut_for_save = initial_keybind.clone();
|
||||||
|
|
||||||
if let Err(e) = register_shortcut(&initial_shortcut) {
|
if let Err(e) = register_shortcut(&initial_keybind) {
|
||||||
eprintln!("Error registering initial shortcut: {:?}", e);
|
eprintln!("Error registering initial shortcut: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
app_handle.listen("update-shortcut", move |event| {
|
app_handle.listen("update-shortcut", move |event| {
|
||||||
let payload_str = event.payload().to_string();
|
let payload_str = event.payload();
|
||||||
|
|
||||||
if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_update) {
|
if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_update) {
|
||||||
HOTKEY_MANAGER.with(|manager| {
|
let manager_guard = HOTKEY_MANAGER.lock().unwrap();
|
||||||
if let Some(manager) = manager.borrow().as_ref() {
|
if let Some(manager) = manager_guard.as_ref() {
|
||||||
let _ = manager.unregister(old_hotkey);
|
let _ = manager.unregister(old_hotkey);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = register_shortcut(&payload_str) {
|
let payload: Vec<String> = serde_json::from_str(payload_str).unwrap_or_default();
|
||||||
|
|
||||||
|
if let Err(e) = register_shortcut(&payload) {
|
||||||
eprintln!("Error re-registering shortcut: {:?}", e);
|
eprintln!("Error re-registering shortcut: {:?}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -51,14 +64,14 @@ pub fn setup(app_handle: tauri::AppHandle) {
|
||||||
let payload_str = event.payload().to_string();
|
let payload_str = event.payload().to_string();
|
||||||
|
|
||||||
if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_save) {
|
if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_save) {
|
||||||
HOTKEY_MANAGER.with(|manager| {
|
let manager_guard = HOTKEY_MANAGER.lock().unwrap();
|
||||||
if let Some(manager) = manager.borrow().as_ref() {
|
if let Some(manager) = manager_guard.as_ref() {
|
||||||
let _ = manager.unregister(old_hotkey);
|
let _ = manager.unregister(old_hotkey);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = register_shortcut(&payload_str) {
|
let payload: Vec<String> = serde_json::from_str(&payload_str).unwrap_or_default();
|
||||||
|
if let Err(e) = register_shortcut(&payload) {
|
||||||
eprintln!("Error registering saved shortcut: {:?}", e);
|
eprintln!("Error registering saved shortcut: {:?}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -81,48 +94,36 @@ pub fn setup(app_handle: tauri::AppHandle) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_shortcut(shortcut: &str) -> Result<(), Box<dyn std::error::Error>> {
|
fn register_shortcut(shortcut: &[String]) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let hotkey = parse_hotkey(shortcut)?;
|
let hotkey = parse_hotkey(shortcut)?;
|
||||||
HOTKEY_MANAGER.with(|manager| {
|
|
||||||
if let Some(manager) = manager.borrow().as_ref() {
|
let manager_guard = HOTKEY_MANAGER.lock().unwrap();
|
||||||
manager.register(hotkey)?;
|
if let Some(manager) = manager_guard.as_ref() {
|
||||||
}
|
manager.register(hotkey.clone())?;
|
||||||
|
REGISTERED_HOTKEYS.lock().unwrap().push(hotkey);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
} else {
|
||||||
|
Err("Hotkey manager not initialized".into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_hotkey(shortcut: &str) -> Result<HotKey, Box<dyn std::error::Error>> {
|
fn parse_hotkey(shortcut: &[String]) -> 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;
|
||||||
|
|
||||||
let shortcut = shortcut.replace("\"", "");
|
for part in shortcut {
|
||||||
|
|
||||||
for part in shortcut.split('+') {
|
|
||||||
let part = part.trim().to_lowercase();
|
|
||||||
match part.as_str() {
|
match part.as_str() {
|
||||||
"ctrl" | "control" | "controlleft" => modifiers |= Modifiers::CONTROL,
|
"ControlLeft" => modifiers |= Modifiers::CONTROL,
|
||||||
"alt" | "altleft" | "optionleft" => modifiers |= Modifiers::ALT,
|
"AltLeft" => modifiers |= Modifiers::ALT,
|
||||||
"shift" | "shiftleft" => modifiers |= Modifiers::SHIFT,
|
"ShiftLeft" => modifiers |= Modifiers::SHIFT,
|
||||||
"super" | "meta" | "cmd" | "metaleft" => modifiers |= Modifiers::META,
|
"MetaLeft" => modifiers |= Modifiers::META,
|
||||||
key => {
|
key => {
|
||||||
let key_code = if key.starts_with("key") {
|
code = Some(Code::from(KeyCode::from_str(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))?,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_code =
|
let key_code = code.ok_or_else(|| "No valid key code found".to_string())?;
|
||||||
code.ok_or_else(|| format!("No valid key code found in shortcut: {}", shortcut))?;
|
|
||||||
Ok(HotKey::new(Some(modifiers), key_code))
|
Ok(HotKey::new(Some(modifiers), key_code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +145,10 @@ fn handle_hotkey_event(app_handle: &AppHandle) {
|
||||||
center_window_on_current_monitor(&window);
|
center_window_on_current_monitor(&window);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = app_handle.track_event("hotkey_triggered", Some(serde_json::json!({
|
let _ = app_handle.track_event(
|
||||||
"action": if window.is_visible().unwrap() { "hide" } else { "show" }
|
"hotkey_triggered",
|
||||||
})));
|
Some(serde_json::json!({
|
||||||
|
"action": if window.is_visible().unwrap() { "hide" } else { "show" }
|
||||||
|
})),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,8 @@ pub async fn save_keybind(
|
||||||
pool: tauri::State<'_, SqlitePool>,
|
pool: tauri::State<'_, SqlitePool>,
|
||||||
keybind: Vec<String>,
|
keybind: Vec<String>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let keybind_str = keybind.join("+");
|
|
||||||
let keybind_clone = keybind_str.clone();
|
|
||||||
|
|
||||||
app_handle
|
app_handle
|
||||||
.emit("update-shortcut", &keybind_str)
|
.emit("update-shortcut", &keybind)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let json = serde_json::to_string(&keybind).map_err(|e| e.to_string())?;
|
let json = serde_json::to_string(&keybind).map_err(|e| e.to_string())?;
|
||||||
|
@ -46,7 +43,7 @@ pub async fn save_keybind(
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let _ = app_handle.track_event("keybind_saved", Some(serde_json::json!({
|
let _ = app_handle.track_event("keybind_saved", Some(serde_json::json!({
|
||||||
"keybind": keybind_clone
|
"keybind": keybind
|
||||||
})));
|
})));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -97,7 +94,7 @@ pub async fn get_keybind(app_handle: tauri::AppHandle) -> Result<Vec<String>, St
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let json = row.map(|r| r.get::<String, _>("value")).unwrap_or_else(|| {
|
let json = row.map(|r| r.get::<String, _>("value")).unwrap_or_else(|| {
|
||||||
serde_json::to_string(&vec!["Meta".to_string(), "V".to_string()])
|
serde_json::to_string(&vec!["MetaLeft".to_string(), "KeyV".to_string()])
|
||||||
.expect("Failed to serialize default keybind")
|
.expect("Failed to serialize default keybind")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
118
src-tauri/src/utils/keys.rs
Normal file
118
src-tauri/src/utils/keys.rs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
use global_hotkey::hotkey::Code;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub struct KeyCode(Code);
|
||||||
|
|
||||||
|
impl FromStr for KeyCode {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let code = match s {
|
||||||
|
"Backquote" => Code::Backquote,
|
||||||
|
"Backslash" => Code::Backslash,
|
||||||
|
"BracketLeft" => Code::BracketLeft,
|
||||||
|
"BracketRight" => Code::BracketRight,
|
||||||
|
"Comma" => Code::Comma,
|
||||||
|
"Digit0" => Code::Digit0,
|
||||||
|
"Digit1" => Code::Digit1,
|
||||||
|
"Digit2" => Code::Digit2,
|
||||||
|
"Digit3" => Code::Digit3,
|
||||||
|
"Digit4" => Code::Digit4,
|
||||||
|
"Digit5" => Code::Digit5,
|
||||||
|
"Digit6" => Code::Digit6,
|
||||||
|
"Digit7" => Code::Digit7,
|
||||||
|
"Digit8" => Code::Digit8,
|
||||||
|
"Digit9" => Code::Digit9,
|
||||||
|
"Equal" => Code::Equal,
|
||||||
|
"KeyA" => Code::KeyA,
|
||||||
|
"KeyB" => Code::KeyB,
|
||||||
|
"KeyC" => Code::KeyC,
|
||||||
|
"KeyD" => Code::KeyD,
|
||||||
|
"KeyE" => Code::KeyE,
|
||||||
|
"KeyF" => Code::KeyF,
|
||||||
|
"KeyG" => Code::KeyG,
|
||||||
|
"KeyH" => Code::KeyH,
|
||||||
|
"KeyI" => Code::KeyI,
|
||||||
|
"KeyJ" => Code::KeyJ,
|
||||||
|
"KeyK" => Code::KeyK,
|
||||||
|
"KeyL" => Code::KeyL,
|
||||||
|
"KeyM" => Code::KeyM,
|
||||||
|
"KeyN" => Code::KeyN,
|
||||||
|
"KeyO" => Code::KeyO,
|
||||||
|
"KeyP" => Code::KeyP,
|
||||||
|
"KeyQ" => Code::KeyQ,
|
||||||
|
"KeyR" => Code::KeyR,
|
||||||
|
"KeyS" => Code::KeyS,
|
||||||
|
"KeyT" => Code::KeyT,
|
||||||
|
"KeyU" => Code::KeyU,
|
||||||
|
"KeyV" => Code::KeyV,
|
||||||
|
"KeyW" => Code::KeyW,
|
||||||
|
"KeyX" => Code::KeyX,
|
||||||
|
"KeyY" => Code::KeyY,
|
||||||
|
"KeyZ" => Code::KeyZ,
|
||||||
|
"Minus" => Code::Minus,
|
||||||
|
"Period" => Code::Period,
|
||||||
|
"Quote" => Code::Quote,
|
||||||
|
"Semicolon" => Code::Semicolon,
|
||||||
|
"Slash" => Code::Slash,
|
||||||
|
"Backspace" => Code::Backspace,
|
||||||
|
"CapsLock" => Code::CapsLock,
|
||||||
|
"Delete" => Code::Delete,
|
||||||
|
"Enter" => Code::Enter,
|
||||||
|
"Space" => Code::Space,
|
||||||
|
"Tab" => Code::Tab,
|
||||||
|
"End" => Code::End,
|
||||||
|
"Home" => Code::Home,
|
||||||
|
"Insert" => Code::Insert,
|
||||||
|
"PageDown" => Code::PageDown,
|
||||||
|
"PageUp" => Code::PageUp,
|
||||||
|
"ArrowDown" => Code::ArrowDown,
|
||||||
|
"ArrowLeft" => Code::ArrowLeft,
|
||||||
|
"ArrowRight" => Code::ArrowRight,
|
||||||
|
"ArrowUp" => Code::ArrowUp,
|
||||||
|
"NumLock" => Code::NumLock,
|
||||||
|
"Numpad0" => Code::Numpad0,
|
||||||
|
"Numpad1" => Code::Numpad1,
|
||||||
|
"Numpad2" => Code::Numpad2,
|
||||||
|
"Numpad3" => Code::Numpad3,
|
||||||
|
"Numpad4" => Code::Numpad4,
|
||||||
|
"Numpad5" => Code::Numpad5,
|
||||||
|
"Numpad6" => Code::Numpad6,
|
||||||
|
"Numpad7" => Code::Numpad7,
|
||||||
|
"Numpad8" => Code::Numpad8,
|
||||||
|
"Numpad9" => Code::Numpad9,
|
||||||
|
"NumpadAdd" => Code::NumpadAdd,
|
||||||
|
"NumpadDecimal" => Code::NumpadDecimal,
|
||||||
|
"NumpadDivide" => Code::NumpadDivide,
|
||||||
|
"NumpadMultiply" => Code::NumpadMultiply,
|
||||||
|
"NumpadSubtract" => Code::NumpadSubtract,
|
||||||
|
"Escape" => Code::Escape,
|
||||||
|
"PrintScreen" => Code::PrintScreen,
|
||||||
|
"ScrollLock" => Code::ScrollLock,
|
||||||
|
"Pause" => Code::Pause,
|
||||||
|
"AudioVolumeDown" => Code::AudioVolumeDown,
|
||||||
|
"AudioVolumeMute" => Code::AudioVolumeMute,
|
||||||
|
"AudioVolumeUp" => Code::AudioVolumeUp,
|
||||||
|
"F1" => Code::F1,
|
||||||
|
"F2" => Code::F2,
|
||||||
|
"F3" => Code::F3,
|
||||||
|
"F4" => Code::F4,
|
||||||
|
"F5" => Code::F5,
|
||||||
|
"F6" => Code::F6,
|
||||||
|
"F7" => Code::F7,
|
||||||
|
"F8" => Code::F8,
|
||||||
|
"F9" => Code::F9,
|
||||||
|
"F10" => Code::F10,
|
||||||
|
"F11" => Code::F11,
|
||||||
|
"F12" => Code::F12,
|
||||||
|
_ => return Err(format!("Unknown key code: {}", s)),
|
||||||
|
};
|
||||||
|
Ok(KeyCode(code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<KeyCode> for Code {
|
||||||
|
fn from(key_code: KeyCode) -> Self {
|
||||||
|
key_code.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,3 +2,4 @@ pub mod commands;
|
||||||
pub mod favicon;
|
pub mod favicon;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
|
pub mod keys;
|
217
types/keys.ts
Normal file
217
types/keys.ts
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
export enum KeyValues {
|
||||||
|
Backquote = 'Backquote',
|
||||||
|
Backslash = 'Backslash',
|
||||||
|
BracketLeft = 'BracketLeft',
|
||||||
|
BracketRight = 'BracketRight',
|
||||||
|
Comma = 'Comma',
|
||||||
|
Digit0 = 'Digit0',
|
||||||
|
Digit1 = 'Digit1',
|
||||||
|
Digit2 = 'Digit2',
|
||||||
|
Digit3 = 'Digit3',
|
||||||
|
Digit4 = 'Digit4',
|
||||||
|
Digit5 = 'Digit5',
|
||||||
|
Digit6 = 'Digit6',
|
||||||
|
Digit7 = 'Digit7',
|
||||||
|
Digit8 = 'Digit8',
|
||||||
|
Digit9 = 'Digit9',
|
||||||
|
Equal = 'Equal',
|
||||||
|
KeyA = 'KeyA',
|
||||||
|
KeyB = 'KeyB',
|
||||||
|
KeyC = 'KeyC',
|
||||||
|
KeyD = 'KeyD',
|
||||||
|
KeyE = 'KeyE',
|
||||||
|
KeyF = 'KeyF',
|
||||||
|
KeyG = 'KeyG',
|
||||||
|
KeyH = 'KeyH',
|
||||||
|
KeyI = 'KeyI',
|
||||||
|
KeyJ = 'KeyJ',
|
||||||
|
KeyK = 'KeyK',
|
||||||
|
KeyL = 'KeyL',
|
||||||
|
KeyM = 'KeyM',
|
||||||
|
KeyN = 'KeyN',
|
||||||
|
KeyO = 'KeyO',
|
||||||
|
KeyP = 'KeyP',
|
||||||
|
KeyQ = 'KeyQ',
|
||||||
|
KeyR = 'KeyR',
|
||||||
|
KeyS = 'KeyS',
|
||||||
|
KeyT = 'KeyT',
|
||||||
|
KeyU = 'KeyU',
|
||||||
|
KeyV = 'KeyV',
|
||||||
|
KeyW = 'KeyW',
|
||||||
|
KeyX = 'KeyX',
|
||||||
|
KeyY = 'KeyY',
|
||||||
|
KeyZ = 'KeyZ',
|
||||||
|
Minus = 'Minus',
|
||||||
|
Period = 'Period',
|
||||||
|
Quote = 'Quote',
|
||||||
|
Semicolon = 'Semicolon',
|
||||||
|
Slash = 'Slash',
|
||||||
|
AltLeft = 'AltLeft',
|
||||||
|
AltRight = 'AltRight',
|
||||||
|
Backspace = 'Backspace',
|
||||||
|
CapsLock = 'CapsLock',
|
||||||
|
ContextMenu = 'ContextMenu',
|
||||||
|
ControlLeft = 'ControlLeft',
|
||||||
|
ControlRight = 'ControlRight',
|
||||||
|
Enter = 'Enter',
|
||||||
|
MetaLeft = 'MetaLeft',
|
||||||
|
MetaRight = 'MetaRight',
|
||||||
|
ShiftLeft = 'ShiftLeft',
|
||||||
|
ShiftRight = 'ShiftRight',
|
||||||
|
Space = 'Space',
|
||||||
|
Tab = 'Tab',
|
||||||
|
Delete = 'Delete',
|
||||||
|
End = 'End',
|
||||||
|
Home = 'Home',
|
||||||
|
Insert = 'Insert',
|
||||||
|
PageDown = 'PageDown',
|
||||||
|
PageUp = 'PageUp',
|
||||||
|
ArrowDown = 'ArrowDown',
|
||||||
|
ArrowLeft = 'ArrowLeft',
|
||||||
|
ArrowRight = 'ArrowRight',
|
||||||
|
ArrowUp = 'ArrowUp',
|
||||||
|
NumLock = 'NumLock',
|
||||||
|
Numpad0 = 'Numpad0',
|
||||||
|
Numpad1 = 'Numpad1',
|
||||||
|
Numpad2 = 'Numpad2',
|
||||||
|
Numpad3 = 'Numpad3',
|
||||||
|
Numpad4 = 'Numpad4',
|
||||||
|
Numpad5 = 'Numpad5',
|
||||||
|
Numpad6 = 'Numpad6',
|
||||||
|
Numpad7 = 'Numpad7',
|
||||||
|
Numpad8 = 'Numpad8',
|
||||||
|
Numpad9 = 'Numpad9',
|
||||||
|
NumpadAdd = 'NumpadAdd',
|
||||||
|
NumpadDecimal = 'NumpadDecimal',
|
||||||
|
NumpadDivide = 'NumpadDivide',
|
||||||
|
NumpadMultiply = 'NumpadMultiply',
|
||||||
|
NumpadSubtract = 'NumpadSubtract',
|
||||||
|
Escape = 'Escape',
|
||||||
|
PrintScreen = 'PrintScreen',
|
||||||
|
ScrollLock = 'ScrollLock',
|
||||||
|
Pause = 'Pause',
|
||||||
|
AudioVolumeDown = 'AudioVolumeDown',
|
||||||
|
AudioVolumeMute = 'AudioVolumeMute',
|
||||||
|
AudioVolumeUp = 'AudioVolumeUp',
|
||||||
|
F1 = 'F1',
|
||||||
|
F2 = 'F2',
|
||||||
|
F3 = 'F3',
|
||||||
|
F4 = 'F4',
|
||||||
|
F5 = 'F5',
|
||||||
|
F6 = 'F6',
|
||||||
|
F7 = 'F7',
|
||||||
|
F8 = 'F8',
|
||||||
|
F9 = 'F9',
|
||||||
|
F10 = 'F10',
|
||||||
|
F11 = 'F11',
|
||||||
|
F12 = 'F12',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum KeyLabels {
|
||||||
|
Backquote = '`',
|
||||||
|
Backslash = '\\',
|
||||||
|
BracketLeft = '[',
|
||||||
|
BracketRight = ']',
|
||||||
|
Comma = ',',
|
||||||
|
Digit0 = '0',
|
||||||
|
Digit1 = '1',
|
||||||
|
Digit2 = '2',
|
||||||
|
Digit3 = '3',
|
||||||
|
Digit4 = '4',
|
||||||
|
Digit5 = '5',
|
||||||
|
Digit6 = '6',
|
||||||
|
Digit7 = '7',
|
||||||
|
Digit8 = '8',
|
||||||
|
Digit9 = '9',
|
||||||
|
Equal = '=',
|
||||||
|
KeyA = 'A',
|
||||||
|
KeyB = 'B',
|
||||||
|
KeyC = 'C',
|
||||||
|
KeyD = 'D',
|
||||||
|
KeyE = 'E',
|
||||||
|
KeyF = 'F',
|
||||||
|
KeyG = 'G',
|
||||||
|
KeyH = 'H',
|
||||||
|
KeyI = 'I',
|
||||||
|
KeyJ = 'J',
|
||||||
|
KeyK = 'K',
|
||||||
|
KeyL = 'L',
|
||||||
|
KeyM = 'M',
|
||||||
|
KeyN = 'N',
|
||||||
|
KeyO = 'O',
|
||||||
|
KeyP = 'P',
|
||||||
|
KeyQ = 'Q',
|
||||||
|
KeyR = 'R',
|
||||||
|
KeyS = 'S',
|
||||||
|
KeyT = 'T',
|
||||||
|
KeyU = 'U',
|
||||||
|
KeyV = 'V',
|
||||||
|
KeyW = 'W',
|
||||||
|
KeyX = 'X',
|
||||||
|
KeyY = 'Y',
|
||||||
|
KeyZ = 'Z',
|
||||||
|
Minus = '-',
|
||||||
|
Period = '.',
|
||||||
|
Quote = "'",
|
||||||
|
Semicolon = ';',
|
||||||
|
Slash = '/',
|
||||||
|
AltLeft = 'Alt',
|
||||||
|
AltRight = 'Alt (Right)',
|
||||||
|
Backspace = 'Backspace',
|
||||||
|
CapsLock = 'Caps Lock',
|
||||||
|
ContextMenu = 'Context Menu',
|
||||||
|
ControlLeft = 'Ctrl',
|
||||||
|
ControlRight = 'Ctrl (Right)',
|
||||||
|
Enter = 'Enter',
|
||||||
|
MetaLeft = 'Meta',
|
||||||
|
MetaRight = 'Meta (Right)',
|
||||||
|
ShiftLeft = 'Shift',
|
||||||
|
ShiftRight = 'Shift (Right)',
|
||||||
|
Space = 'Space',
|
||||||
|
Tab = 'Tab',
|
||||||
|
Delete = 'Delete',
|
||||||
|
End = 'End',
|
||||||
|
Home = 'Home',
|
||||||
|
Insert = 'Insert',
|
||||||
|
PageDown = 'Page Down',
|
||||||
|
PageUp = 'Page Up',
|
||||||
|
ArrowDown = '↓',
|
||||||
|
ArrowLeft = '←',
|
||||||
|
ArrowRight = '→',
|
||||||
|
ArrowUp = '↑',
|
||||||
|
NumLock = 'Num Lock',
|
||||||
|
Numpad0 = 'Numpad 0',
|
||||||
|
Numpad1 = 'Numpad 1',
|
||||||
|
Numpad2 = 'Numpad 2',
|
||||||
|
Numpad3 = 'Numpad 3',
|
||||||
|
Numpad4 = 'Numpad 4',
|
||||||
|
Numpad5 = 'Numpad 5',
|
||||||
|
Numpad6 = 'Numpad 6',
|
||||||
|
Numpad7 = 'Numpad 7',
|
||||||
|
Numpad8 = 'Numpad 8',
|
||||||
|
Numpad9 = 'Numpad 9',
|
||||||
|
NumpadAdd = 'Numpad +',
|
||||||
|
NumpadDecimal = 'Numpad .',
|
||||||
|
NumpadDivide = 'Numpad /',
|
||||||
|
NumpadMultiply = 'Numpad *',
|
||||||
|
NumpadSubtract = 'Numpad -',
|
||||||
|
Escape = 'Esc',
|
||||||
|
PrintScreen = 'Print Screen',
|
||||||
|
ScrollLock = 'Scroll Lock',
|
||||||
|
Pause = 'Pause',
|
||||||
|
AudioVolumeDown = 'Volume Down',
|
||||||
|
AudioVolumeMute = 'Volume Mute',
|
||||||
|
AudioVolumeUp = 'Volume Up',
|
||||||
|
F1 = 'F1',
|
||||||
|
F2 = 'F2',
|
||||||
|
F3 = 'F3',
|
||||||
|
F4 = 'F4',
|
||||||
|
F5 = 'F5',
|
||||||
|
F6 = 'F6',
|
||||||
|
F7 = 'F7',
|
||||||
|
F8 = 'F8',
|
||||||
|
F9 = 'F9',
|
||||||
|
F10 = 'F10',
|
||||||
|
F11 = 'F11',
|
||||||
|
F12 = 'F12',
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue