mirror of
https://github.com/0PandaDEV/Qopy.git
synced 2025-04-22 05:34:04 +02:00
added ability to set custom hotkey
This commit is contained in:
parent
79dd783bf7
commit
5bb6f2249a
8 changed files with 250 additions and 205 deletions
|
@ -17,50 +17,133 @@ $mutedtext: #78756F;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.back {
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
img{
|
||||||
|
background-color: $divider;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: $text2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.keybind-container {
|
.keybind-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
padding: 20px;
|
gap: 6px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keybind-input {
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid $divider;
|
||||||
|
color: $text2;
|
||||||
|
display: flex;
|
||||||
|
border-radius: 13px;
|
||||||
|
outline: none;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.key {
|
||||||
|
color: $text2;
|
||||||
|
font-family: SFRoundedMedium;
|
||||||
|
background-color: $divider;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.keybind-input:focus {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
.bottom-bar {
|
||||||
margin-bottom: 20px;
|
height: 40px;
|
||||||
}
|
width: calc(100vw - 2px);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
background-color: hsla(40, 3%, 16%, 0.8);
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1px;
|
||||||
|
left: 1px;
|
||||||
|
z-index: 100;
|
||||||
|
border-radius: 0 0 12px 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-inline: 12px;
|
||||||
|
padding-right: 6px;
|
||||||
|
padding-top: 1px;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
border-top: 1px solid $divider;
|
||||||
|
|
||||||
.keybind-input {
|
p {
|
||||||
width: 300px;
|
color: $text2;
|
||||||
height: 50px;
|
}
|
||||||
border: 2px solid $accent;
|
|
||||||
border-radius: 5px;
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 8px;
|
||||||
font-size: 18px;
|
|
||||||
|
.logo {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.actions div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 2px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: $divider;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 4px;
|
||||||
|
transition: all .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
padding: 4px;
|
||||||
|
padding-left: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
border-radius: 7px;
|
||||||
|
background-color: transparent;
|
||||||
|
transition: all .2s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-bottom: 20px;
|
}
|
||||||
background-color: rgba($accent, 0.1);
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.keybind-input:focus {
|
.actions:hover {
|
||||||
outline: none;
|
background-color: $divider;
|
||||||
box-shadow: 0 0 0 2px rgba($accent, 0.5);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
&:hover .actions:hover~.divider {
|
||||||
padding: 10px 20px;
|
opacity: 0;
|
||||||
background-color: $accent;
|
}
|
||||||
color: $primary;
|
}
|
||||||
border: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
}
|
|
@ -454,6 +454,6 @@ onMounted(async () => {
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~/assets/css/index.scss';
|
@import '~/assets/css/index.scss';
|
||||||
</style>
|
</style>
|
|
@ -1,23 +1,52 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="bg">
|
<div class="bg">
|
||||||
<div class="keybind-container">
|
<div class="back">
|
||||||
<h2>Set New Keybind</h2>
|
<img @click="router.push('/')" src="../public/back_arrow.svg">
|
||||||
<div class="keybind-input" tabindex="0" @keydown="onKeyDown" @keyup="onKeyUp" @focus="onFocus" ref="keybindInput">
|
<p>Back</p>
|
||||||
{{ currentKeybind || 'Click here, then press your desired key combination' }}
|
</div>
|
||||||
|
<div class="bottom-bar">
|
||||||
|
<div class="left">
|
||||||
|
<img alt="" class="logo" src="../public/logo.png" width="18px">
|
||||||
|
<p>Qopy</p>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<div @click="saveKeybind" class="actions">
|
||||||
|
<p>Save</p>
|
||||||
|
<div>
|
||||||
|
<img alt="" src="../public/ctrl.svg" v-if="os === 'windows' || os === 'linux'">
|
||||||
|
<img alt="" src="../public/cmd.svg" v-if="os === 'macos'">
|
||||||
|
<img alt="" src="../public/enter.svg">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="keybind-container">
|
||||||
|
<h2 class="title">Record a new Hotkey</h2>
|
||||||
|
<div @blur="onBlur" @focus="onFocus" @keydown="onKeyDown" @keyup="onKeyUp" class="keybind-input"
|
||||||
|
ref="keybindInput" tabindex="0">
|
||||||
|
<span class="key" v-if="currentKeybind.length === 0">Click here</span>
|
||||||
|
<template v-else>
|
||||||
|
<span :key="index" class="key" v-for="(key, index) in currentKeybind">{{ keyToDisplay(key) }}</span>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<button @click="saveKeybind" :disabled="!currentKeybind">Save Keybind</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import { platform } from '@tauri-apps/plugin-os';
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
const currentKeybind = ref('');
|
const activeModifiers = ref<Set<string>>(new Set());
|
||||||
|
const currentKeybind = ref<string[]>([]);
|
||||||
|
const isKeybindInputFocused = ref(false);
|
||||||
const keybindInput = ref<HTMLElement | null>(null);
|
const keybindInput = ref<HTMLElement | null>(null);
|
||||||
const keys = ref<Set<string>>(new Set());
|
const lastNonModifier = ref('');
|
||||||
const recording = ref(false);
|
const os = ref('');
|
||||||
|
const router = useRouter();
|
||||||
|
const lastBlurTime = ref(0);
|
||||||
|
|
||||||
const keyToDisplayMap: Record<string, string> = {
|
const keyToDisplayMap: Record<string, string> = {
|
||||||
" ": "Space",
|
" ": "Space",
|
||||||
|
@ -28,78 +57,83 @@ const keyToDisplayMap: Record<string, string> = {
|
||||||
ArrowUp: "↑",
|
ArrowUp: "↑",
|
||||||
Control: "Ctrl",
|
Control: "Ctrl",
|
||||||
Enter: "↵",
|
Enter: "↵",
|
||||||
Escape: "Esc",
|
|
||||||
Meta: "Meta",
|
Meta: "Meta",
|
||||||
Shift: "⇧",
|
Shift: "⇧",
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifierKeySet = new Set(["Alt", "Control", "Meta", "Shift"]);
|
const modifierKeySet = new Set(["Alt", "Control", "Meta", "Shift"]);
|
||||||
|
|
||||||
function keyCodeToKey(keyCode: string): string {
|
function keyToDisplay(key: string): string {
|
||||||
if (keyCode.startsWith("Key")) return keyCode.slice(3);
|
return keyToDisplayMap[key] || key.toUpperCase();
|
||||||
if (keyCode.endsWith("Left")) return keyCode.slice(0, -4);
|
|
||||||
if (keyCode.startsWith("Digit")) return keyCode.slice(5);
|
|
||||||
if (keyCode.endsWith("Right")) return keyCode.slice(0, -5);
|
|
||||||
return keyCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyToDisplay(keyCode: string): string {
|
function updateCurrentKeybind() {
|
||||||
const key = keyCodeToKey(keyCode);
|
const modifiers = Array.from(activeModifiers.value);
|
||||||
return keyToDisplayMap[key] || key;
|
currentKeybind.value = lastNonModifier.value ? [...modifiers, lastNonModifier.value] : modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyCombToDisplay(keyComb: Set<string>): string {
|
const onBlur = () => {
|
||||||
return Array.from(keyComb).map(keyToDisplay).join("+");
|
isKeybindInputFocused.value = false;
|
||||||
}
|
lastBlurTime.value = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
function mapKeyToTauriKey(key: string): string {
|
const onFocus = () => {
|
||||||
return key === "Meta" ? "Command" : key;
|
isKeybindInputFocused.value = true;
|
||||||
}
|
activeModifiers.value.clear();
|
||||||
|
lastNonModifier.value = '';
|
||||||
|
updateCurrentKeybind();
|
||||||
|
};
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const key = keyCodeToKey(event.code);
|
const key = event.key;
|
||||||
|
|
||||||
if (modifierKeySet.has(key) && !keys.value.has(key)) {
|
if (key === "Escape") {
|
||||||
keys.value = new Set(Array.from(keys.value).filter(k => modifierKeySet.has(k)));
|
if (keybindInput.value) {
|
||||||
|
keybindInput.value.blur();
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
keys.value.add(key);
|
if (modifierKeySet.has(key)) {
|
||||||
|
activeModifiers.value.add(key);
|
||||||
|
} else {
|
||||||
|
lastNonModifier.value = key;
|
||||||
|
}
|
||||||
updateCurrentKeybind();
|
updateCurrentKeybind();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKeyUp = (event: KeyboardEvent) => {
|
const onKeyUp = (event: KeyboardEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const key = keyCodeToKey(event.code);
|
|
||||||
if (!modifierKeySet.has(key)) {
|
|
||||||
recording.value = false;
|
|
||||||
updateCurrentKeybind();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFocus = () => {
|
|
||||||
resetKeybind();
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateCurrentKeybind = () => {
|
|
||||||
currentKeybind.value = keyCombToDisplay(keys.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetKeybind = () => {
|
|
||||||
keys.value.clear();
|
|
||||||
currentKeybind.value = '';
|
|
||||||
recording.value = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveKeybind = async () => {
|
const saveKeybind = async () => {
|
||||||
console.log(await invoke("get_keybind"));
|
console.log("New:", currentKeybind.value);
|
||||||
|
console.log("Old: " + new Array(await invoke("get_keybind")));
|
||||||
|
await invoke("save_keybind", { keybind: currentKeybind.value})
|
||||||
};
|
};
|
||||||
|
|
||||||
const startCapture = () => {
|
const handleGlobalKeyDown = (event: KeyboardEvent) => {
|
||||||
resetKeybind();
|
const now = Date.now();
|
||||||
|
if ((os.value === 'macos' ? event.metaKey : event.ctrlKey) && event.key === 'Enter' && !isKeybindInputFocused.value) {
|
||||||
|
event.preventDefault();
|
||||||
|
saveKeybind();
|
||||||
|
} else if (event.key === 'Escape' && !isKeybindInputFocused.value && now - lastBlurTime.value > 100) {
|
||||||
|
event.preventDefault();
|
||||||
|
router.push('/');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
os.value = platform();
|
||||||
|
window.addEventListener('keydown', handleGlobalKeyDown);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('keydown', handleGlobalKeyDown);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~/assets/css/keybind.scss';
|
@import '~/assets/css/keybind.scss';
|
||||||
</style>
|
</style>
|
7
public/back_arrow.svg
Normal file
7
public/back_arrow.svg
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" stroke="none" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="Arrow" transform="translate(0 0)">
|
||||||
|
<rect id="Rectangle" width="12" height="8" style="mix-blend-mode:normal;" transform="translate(0 0)" />
|
||||||
|
<path id="Path" d="M3.10928 8.76192C3.10928 8.76192 3.10928 0.952386 3.10928 0.952386C3.10928 0.682544 3.1892 0.456512 3.34903 0.274291C3.50887 0.0920672 3.70643 0.000639439 3.94173 3.33786e-06C4.17706 -0.000631809 4.37489 0.0907969 4.53526 0.274291C4.69566 0.457782 4.77529 0.683815 4.77418 0.952386C4.77418 0.952386 4.77418 8.76192 4.77418 8.76192C4.77418 8.76192 6.60566 7 6.60566 7C6.75827 6.8254 6.95253 6.73811 7.18839 6.73811C7.42426 6.73811 7.61849 6.8254 7.77111 7C7.92373 7.1746 8 7.39683 8 7.66667C8 7.93651 7.92373 8.15874 7.77111 8.33334C7.77111 8.33334 4.52444 11.7143 4.52444 11.7143C4.35797 11.9048 4.16372 12 3.94173 12C3.71975 12 3.52551 11.9048 3.35902 11.7143C3.35902 11.7143 0.228926 8.33334 0.228926 8.33334C0.076309 8.15874 0 7.93652 0 7.66668C0 7.39683 0.076309 7.1746 0.228926 7C0.381543 6.8254 0.575782 6.73811 0.811648 6.73811C1.04751 6.73811 1.24175 6.8254 1.39437 7C1.39437 7 3.10928 8.76192 3.10928 8.76192Z" style="fill:#ADA9A1;fill-rule:evenodd;mix-blend-mode:normal;" transform="matrix(0 1 -1 0 12 0)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
@ -121,8 +121,7 @@ pub async fn save_keybind(
|
||||||
keybind: Vec<String>,
|
keybind: Vec<String>,
|
||||||
pool: State<'_, SqlitePool>,
|
pool: State<'_, SqlitePool>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let setting = KeybindSetting { keybind };
|
let json = serde_json::to_string(&keybind).map_err(|e| e.to_string())?;
|
||||||
let json = serde_json::to_string(&setting).map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"INSERT OR REPLACE INTO settings (key, value) VALUES ('keybind', ?)"
|
"INSERT OR REPLACE INTO settings (key, value) VALUES ('keybind', ?)"
|
||||||
|
|
|
@ -1,123 +1,47 @@
|
||||||
use rdev::{listen, Event, EventType, Key};
|
use crate::api::database::get_keybind;
|
||||||
use tauri::{Manager, Emitter};
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::utils::commands::center_window_on_current_monitor;
|
use crate::utils::commands::center_window_on_current_monitor;
|
||||||
|
use rdev::{listen, EventType, Key};
|
||||||
|
use tauri::Manager;
|
||||||
|
|
||||||
static IS_CAPTURING_KEYBIND: AtomicBool = AtomicBool::new(false);
|
fn key_to_string(key: &Key) -> String {
|
||||||
|
format!("{:?}", key)
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
struct CapturedKeybind {
|
|
||||||
modifiers: Vec<String>,
|
|
||||||
key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct KeybindState {
|
|
||||||
pressed_keys: HashSet<Key>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeybindState {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
pressed_keys: HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[warn(dead_code)]
|
||||||
pub fn setup(app_handle: tauri::AppHandle) {
|
pub fn setup(app_handle: tauri::AppHandle) {
|
||||||
let app_handle_clone = app_handle.clone();
|
|
||||||
let keybind_state = Arc::new(Mutex::new(KeybindState::new()));
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
if let Err(e) = listen(move |event| {
|
let pool = app_handle.state::<sqlx::SqlitePool>();
|
||||||
let mut state = keybind_state.lock().unwrap();
|
let rt = app_handle.state::<tokio::runtime::Runtime>();
|
||||||
if IS_CAPTURING_KEYBIND.load(Ordering::SeqCst) {
|
|
||||||
handle_keybind_capture(&app_handle_clone, event, &mut state);
|
|
||||||
} else {
|
|
||||||
handle_normal_hotkey(&app_handle_clone, event, &mut state);
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
eprintln!("Error setting up event listener: {:?}", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_normal_hotkey(app_handle: &tauri::AppHandle, event: Event, state: &mut KeybindState) {
|
let keybind = rt.block_on(async { get_keybind(pool).await.unwrap_or_default() });
|
||||||
match event.event_type {
|
|
||||||
EventType::KeyPress(Key::MetaLeft) | EventType::KeyPress(Key::MetaRight) => {
|
|
||||||
state.pressed_keys.insert(Key::MetaLeft);
|
|
||||||
}
|
|
||||||
EventType::KeyRelease(Key::MetaLeft) | EventType::KeyRelease(Key::MetaRight) => {
|
|
||||||
state.pressed_keys.remove(&Key::MetaLeft);
|
|
||||||
}
|
|
||||||
EventType::KeyPress(Key::KeyV) => {
|
|
||||||
if state.pressed_keys.contains(&Key::MetaLeft) {
|
|
||||||
state.pressed_keys.clear();
|
|
||||||
if let Some(window) = app_handle.get_webview_window("main") {
|
|
||||||
let _ = window.show();
|
|
||||||
let _ = window.set_focus();
|
|
||||||
center_window_on_current_monitor(&window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_keybind_capture(app_handle: &tauri::AppHandle, event: Event, state: &mut KeybindState) {
|
println!("Listening for keybind: {:?}", keybind);
|
||||||
|
|
||||||
|
let mut pressed_keys = vec![false; keybind.len()];
|
||||||
|
|
||||||
|
listen(move |event| {
|
||||||
match event.event_type {
|
match event.event_type {
|
||||||
EventType::KeyPress(key) => {
|
EventType::KeyPress(key) => {
|
||||||
state.pressed_keys.insert(key);
|
if let Some(index) = keybind.iter().position(|k| k == &key_to_string(&key)) {
|
||||||
update_captured_keybind(app_handle, &state.pressed_keys);
|
pressed_keys[index] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EventType::KeyRelease(key) => {
|
EventType::KeyRelease(key) => {
|
||||||
state.pressed_keys.remove(&key);
|
if let Some(index) = keybind.iter().position(|k| k == &key_to_string(&key)) {
|
||||||
|
pressed_keys[index] = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn update_captured_keybind(app_handle: &tauri::AppHandle, pressed_keys: &HashSet<Key>) {
|
if pressed_keys.iter().all(|&k| k) {
|
||||||
let modifiers: Vec<String> = vec![Key::ControlLeft, Key::ShiftLeft, Key::Alt, Key::MetaLeft]
|
pressed_keys.iter_mut().for_each(|k| *k = false);
|
||||||
.into_iter()
|
let window = app_handle.get_webview_window("main").unwrap();
|
||||||
.filter(|key| pressed_keys.contains(key))
|
window.show().unwrap();
|
||||||
.map(|key| key_to_string(key))
|
window.set_focus().unwrap();
|
||||||
.collect();
|
center_window_on_current_monitor(&window);
|
||||||
|
|
||||||
let key = pressed_keys.iter()
|
|
||||||
.find(|&&key| !vec![Key::ControlLeft, Key::ShiftLeft, Key::Alt, Key::MetaLeft].contains(&key))
|
|
||||||
.map(|&key| key_to_string(key));
|
|
||||||
|
|
||||||
if let Some(key) = key {
|
|
||||||
let captured_keybind = CapturedKeybind {
|
|
||||||
modifiers,
|
|
||||||
key,
|
|
||||||
};
|
|
||||||
if let Err(e) = app_handle.emit("keybind_captured", captured_keybind) {
|
|
||||||
eprintln!("Error emitting keybind_captured event: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
.unwrap();
|
||||||
fn key_to_string(key: Key) -> String {
|
});
|
||||||
match key {
|
|
||||||
Key::ControlLeft | Key::ControlRight => "Ctrl".to_string(),
|
|
||||||
Key::ShiftLeft | Key::ShiftRight => "Shift".to_string(),
|
|
||||||
Key::Alt => "Alt".to_string(),
|
|
||||||
Key::MetaLeft | Key::MetaRight => "Meta".to_string(),
|
|
||||||
_ => format!("{:?}", key),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn start_keybind_capture() {
|
|
||||||
IS_CAPTURING_KEYBIND.store(true, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn stop_keybind_capture() {
|
|
||||||
IS_CAPTURING_KEYBIND.store(false, Ordering::SeqCst);
|
|
||||||
}
|
}
|
|
@ -31,7 +31,7 @@ fn main() {
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let app_handle = app.handle().clone();
|
let app_handle = app.handle().clone();
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
// #[cfg(not(target_os = "macos"))]
|
||||||
api::hotkeys::setup(app_handle.clone());
|
api::hotkeys::setup(app_handle.clone());
|
||||||
api::tray::setup(app)?;
|
api::tray::setup(app)?;
|
||||||
let _ = api::database::setup(app);
|
let _ = api::database::setup(app);
|
||||||
|
@ -74,8 +74,6 @@ fn main() {
|
||||||
api::clipboard::get_image_path,
|
api::clipboard::get_image_path,
|
||||||
api::clipboard::write_and_paste,
|
api::clipboard::write_and_paste,
|
||||||
api::clipboard::read_image,
|
api::clipboard::read_image,
|
||||||
api::hotkeys::start_keybind_capture,
|
|
||||||
api::hotkeys::stop_keybind_capture,
|
|
||||||
api::database::save_keybind,
|
api::database::save_keybind,
|
||||||
api::database::get_keybind
|
api::database::get_keybind
|
||||||
])
|
])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue