diff --git a/assets/css/keybind.scss b/assets/css/keybind.scss index e1b871a..c3ad9d4 100644 --- a/assets/css/keybind.scss +++ b/assets/css/keybind.scss @@ -17,50 +17,133 @@ $mutedtext: #78756F; 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 { display: flex; flex-direction: column; align-items: center; justify-content: center; - height: 100%; - padding: 20px; + height: 100vh; + 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 { - margin-bottom: 20px; -} - -.keybind-input { - width: 300px; - height: 50px; - border: 2px solid $accent; - border-radius: 5px; +.bottom-bar { + 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; - justify-content: center; - font-size: 18px; - cursor: pointer; - margin-bottom: 20px; - background-color: rgba($accent, 0.1); - user-select: none; -} + font-size: 14px; + border-top: 1px solid $divider; -.keybind-input:focus { - outline: none; - box-shadow: 0 0 0 2px rgba($accent, 0.5); -} + p { + color: $text2; + } -button { - padding: 10px 20px; - background-color: $accent; - color: $primary; - border: none; - border-radius: 5px; - font-size: 16px; - cursor: pointer; -} + .left { + display: flex; + align-items: center; + gap: 8px; -button:disabled { - opacity: 0.5; - cursor: not-allowed; -} + .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; + } + + .actions:hover { + background-color: $divider; + } + + &:hover .actions:hover~.divider { + opacity: 0; + } + } +} \ No newline at end of file diff --git a/pages/index.vue b/pages/index.vue index e045e51..76cc03e 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -454,6 +454,6 @@ onMounted(async () => { - \ No newline at end of file diff --git a/pages/keybind.vue b/pages/keybind.vue index 2fbc866..3decf40 100644 --- a/pages/keybind.vue +++ b/pages/keybind.vue @@ -1,23 +1,52 @@ - \ No newline at end of file diff --git a/public/back_arrow.svg b/public/back_arrow.svg new file mode 100644 index 0000000..e4c3012 --- /dev/null +++ b/public/back_arrow.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns index 5e955ed..d424395 100644 Binary files a/src-tauri/icons/icon.icns and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/src/api/database.rs b/src-tauri/src/api/database.rs index 4dcaed0..33f15cd 100644 --- a/src-tauri/src/api/database.rs +++ b/src-tauri/src/api/database.rs @@ -121,8 +121,7 @@ pub async fn save_keybind( keybind: Vec, pool: State<'_, SqlitePool>, ) -> Result<(), String> { - let setting = KeybindSetting { keybind }; - let json = serde_json::to_string(&setting).map_err(|e| e.to_string())?; + let json = serde_json::to_string(&keybind).map_err(|e| e.to_string())?; sqlx::query( "INSERT OR REPLACE INTO settings (key, value) VALUES ('keybind', ?)" diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index c0a984a..079355d 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -1,123 +1,47 @@ -use rdev::{listen, Event, EventType, Key}; -use tauri::{Manager, Emitter}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; -use std::collections::HashSet; -use serde::Serialize; - +use crate::api::database::get_keybind; 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); - -#[derive(Debug, Clone, Serialize)] -struct CapturedKeybind { - modifiers: Vec, - key: String, -} - -struct KeybindState { - pressed_keys: HashSet, -} - -impl KeybindState { - fn new() -> Self { - Self { - pressed_keys: HashSet::new(), - } - } +fn key_to_string(key: &Key) -> String { + format!("{:?}", key) } +#[warn(dead_code)] 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 || { - if let Err(e) = listen(move |event| { - let mut state = keybind_state.lock().unwrap(); - 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); - } - }); -} + let pool = app_handle.state::(); + let rt = app_handle.state::(); -fn handle_normal_hotkey(app_handle: &tauri::AppHandle, event: Event, state: &mut KeybindState) { - 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); + let keybind = rt.block_on(async { get_keybind(pool).await.unwrap_or_default() }); + + println!("Listening for keybind: {:?}", keybind); + + let mut pressed_keys = vec![false; keybind.len()]; + + listen(move |event| { + match event.event_type { + EventType::KeyPress(key) => { + if let Some(index) = keybind.iter().position(|k| k == &key_to_string(&key)) { + pressed_keys[index] = true; + } } + EventType::KeyRelease(key) => { + if let Some(index) = keybind.iter().position(|k| k == &key_to_string(&key)) { + pressed_keys[index] = false; + } + } + _ => {} } - } - _ => {} - } -} -fn handle_keybind_capture(app_handle: &tauri::AppHandle, event: Event, state: &mut KeybindState) { - match event.event_type { - EventType::KeyPress(key) => { - state.pressed_keys.insert(key); - update_captured_keybind(app_handle, &state.pressed_keys); - } - EventType::KeyRelease(key) => { - state.pressed_keys.remove(&key); - } - _ => {} - } -} - -fn update_captured_keybind(app_handle: &tauri::AppHandle, pressed_keys: &HashSet) { - let modifiers: Vec = vec![Key::ControlLeft, Key::ShiftLeft, Key::Alt, Key::MetaLeft] - .into_iter() - .filter(|key| pressed_keys.contains(key)) - .map(|key| key_to_string(key)) - .collect(); - - 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); - } - } -} - -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); + if pressed_keys.iter().all(|&k| k) { + pressed_keys.iter_mut().for_each(|k| *k = false); + let window = app_handle.get_webview_window("main").unwrap(); + window.show().unwrap(); + window.set_focus().unwrap(); + center_window_on_current_monitor(&window); + } + }) + .unwrap(); + }); } \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e5c7d81..f1e069a 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -31,7 +31,7 @@ fn main() { .setup(|app| { let app_handle = app.handle().clone(); - #[cfg(not(target_os = "macos"))] + // #[cfg(not(target_os = "macos"))] api::hotkeys::setup(app_handle.clone()); api::tray::setup(app)?; let _ = api::database::setup(app); @@ -74,8 +74,6 @@ fn main() { api::clipboard::get_image_path, api::clipboard::write_and_paste, api::clipboard::read_image, - api::hotkeys::start_keybind_capture, - api::hotkeys::stop_keybind_capture, api::database::save_keybind, api::database::get_keybind ])