Fix focus issue when opening Qopy

Fixes #10

Implement platform-specific focus management to prevent the underlying window from losing focus when the shortcut is pressed.

* **src-tauri/src/api/hotkeys.rs**
  - Add platform-specific modules for Windows, macOS, and Linux to manage focus between windows.
  - Modify the `setup` function to use platform-specific APIs to manage focus between windows.
  - Replace `window.set_focus()` with `platform::manage_focus(&window)`.

* **src-tauri/src/api/tray.rs**
  - Add platform-specific modules for Windows, macOS, and Linux to manage focus between windows when the tray menu is used.
  - Modify the `setup` function to use platform-specific APIs to manage focus between windows.
  - Replace `window_clone_for_tray.set_focus()` with `platform::manage_focus(&window_clone_for_tray)`.

* **app.vue**
  - Add `manageFocus` function to handle platform-specific focus management.
  - Call `manageFocus` function when the `change_keybind` event is triggered.

* **src-tauri/src/main.rs**
  - Remove logic to hide the window when it loses focus.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/0PandaDEV/Qopy/issues/10?shareId=XXXX-XXXX-XXXX-XXXX).
This commit is contained in:
PandaDEV 2024-11-15 12:29:36 +10:00
parent fd0fdd2d51
commit fbd159c68b
4 changed files with 121 additions and 7 deletions

24
app.vue
View file

@ -14,12 +14,36 @@ onMounted(async () => {
await navigateTo('/keybind') await navigateTo('/keybind')
await app.show(); await app.show();
await window.getCurrentWindow().show(); await window.getCurrentWindow().show();
manageFocus();
}) })
await listen('main_route', async () => { await listen('main_route', async () => {
await navigateTo('/') await navigateTo('/')
}) })
}) })
function manageFocus() {
if (process.platform === 'win32') {
const { SetForegroundWindow, AttachThreadInput, GetForegroundWindow, GetWindowThreadProcessId } = require('windows-api');
const foregroundWindow = GetForegroundWindow();
const currentThreadId = GetWindowThreadProcessId(foregroundWindow, null);
const targetThreadId = GetWindowThreadProcessId(window.hwnd(), null);
AttachThreadInput(currentThreadId, targetThreadId, 1);
SetForegroundWindow(window.hwnd());
AttachThreadInput(currentThreadId, targetThreadId, 0);
} else if (process.platform === 'darwin') {
const { NSWindow } = require('cocoa');
const nsWindow = window.ns_window();
nsWindow.makeKeyAndOrderFront(true);
} else if (process.platform === 'linux') {
const { XOpenDisplay, XDefaultRootWindow, XSetInputFocus, XCloseDisplay, RevertToParent } = require('xlib');
const display = XOpenDisplay(null);
const rootWindow = XDefaultRootWindow(display);
XSetInputFocus(display, rootWindow, RevertToParent, 0);
XCloseDisplay(display);
}
}
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -3,6 +3,52 @@ use crate::utils::commands::center_window_on_current_monitor;
use rdev::{listen, EventType, Key}; use rdev::{listen, EventType, Key};
use tauri::Manager; use tauri::Manager;
#[cfg(target_os = "windows")]
mod platform {
use std::ptr::null_mut;
use winapi::um::winuser::{AttachThreadInput, GetForegroundWindow, GetWindowThreadProcessId, SetForegroundWindow};
pub fn manage_focus(window: &tauri::Window) {
unsafe {
let foreground_window = GetForegroundWindow();
let current_thread_id = GetWindowThreadProcessId(foreground_window, null_mut());
let target_thread_id = GetWindowThreadProcessId(window.hwnd() as _, null_mut());
AttachThreadInput(current_thread_id, target_thread_id, 1);
SetForegroundWindow(window.hwnd() as _);
AttachThreadInput(current_thread_id, target_thread_id, 0);
}
}
}
#[cfg(target_os = "macos")]
mod platform {
use cocoa::appkit::NSWindow;
use cocoa::base::id;
use objc::runtime::YES;
pub fn manage_focus(window: &tauri::Window) {
unsafe {
let ns_window: id = window.ns_window().unwrap() as _;
ns_window.makeKeyAndOrderFront_(YES);
}
}
}
#[cfg(target_os = "linux")]
mod platform {
use x11::xlib::{Display, XSetInputFocus, XDefaultRootWindow, XOpenDisplay, XCloseDisplay, RevertToParent};
pub fn manage_focus(window: &tauri::Window) {
unsafe {
let display: *mut Display = XOpenDisplay(null_mut());
let root_window = XDefaultRootWindow(display);
XSetInputFocus(display, root_window, RevertToParent, 0);
XCloseDisplay(display);
}
}
}
fn key_to_string(key: &Key) -> String { fn key_to_string(key: &Key) -> String {
format!("{:?}", key) format!("{:?}", key)
} }
@ -35,8 +81,8 @@ pub fn setup(app_handle: tauri::AppHandle) {
pressed_keys.iter_mut().for_each(|k| *k = false); pressed_keys.iter_mut().for_each(|k| *k = false);
let window = app_handle.get_webview_window("main").unwrap(); let window = app_handle.get_webview_window("main").unwrap();
window.show().unwrap(); window.show().unwrap();
window.set_focus().unwrap();
center_window_on_current_monitor(&window); center_window_on_current_monitor(&window);
platform::manage_focus(&window);
} }
}) })
.unwrap(); .unwrap();

View file

@ -2,6 +2,52 @@ use tauri::{
menu::{MenuBuilder, MenuItemBuilder}, tray::TrayIconBuilder, Emitter, Manager menu::{MenuBuilder, MenuItemBuilder}, tray::TrayIconBuilder, Emitter, Manager
}; };
#[cfg(target_os = "windows")]
mod platform {
use std::ptr::null_mut;
use winapi::um::winuser::{AttachThreadInput, GetForegroundWindow, GetWindowThreadProcessId, SetForegroundWindow};
pub fn manage_focus(window: &tauri::Window) {
unsafe {
let foreground_window = GetForegroundWindow();
let current_thread_id = GetWindowThreadProcessId(foreground_window, null_mut());
let target_thread_id = GetWindowThreadProcessId(window.hwnd() as _, null_mut());
AttachThreadInput(current_thread_id, target_thread_id, 1);
SetForegroundWindow(window.hwnd() as _);
AttachThreadInput(current_thread_id, target_thread_id, 0);
}
}
}
#[cfg(target_os = "macos")]
mod platform {
use cocoa::appkit::NSWindow;
use cocoa::base::id;
use objc::runtime::YES;
pub fn manage_focus(window: &tauri::Window) {
unsafe {
let ns_window: id = window.ns_window().unwrap() as _;
ns_window.makeKeyAndOrderFront_(YES);
}
}
}
#[cfg(target_os = "linux")]
mod platform {
use x11::xlib::{Display, XSetInputFocus, XDefaultRootWindow, XOpenDisplay, XCloseDisplay, RevertToParent};
pub fn manage_focus(window: &tauri::Window) {
unsafe {
let display: *mut Display = XOpenDisplay(null_mut());
let root_window = XDefaultRootWindow(display);
XSetInputFocus(display, root_window, RevertToParent, 0);
XCloseDisplay(display);
}
}
}
pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
let window = app.get_webview_window("main").unwrap(); let window = app.get_webview_window("main").unwrap();
let window_clone_for_tray = window.clone(); let window_clone_for_tray = window.clone();
@ -30,7 +76,7 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
window_clone_for_tray.hide().unwrap(); window_clone_for_tray.hide().unwrap();
} else { } else {
window_clone_for_tray.show().unwrap(); window_clone_for_tray.show().unwrap();
window_clone_for_tray.set_focus().unwrap(); platform::manage_focus(&window_clone_for_tray);
} }
window_clone_for_tray.emit("main_route", ()).unwrap(); window_clone_for_tray.emit("main_route", ()).unwrap();
} }

View file

@ -64,9 +64,7 @@ fn main() {
.on_window_event(|app, event| { .on_window_event(|app, event| {
#[cfg(not(dev))] #[cfg(not(dev))]
if let tauri::WindowEvent::Focused(false) = event { if let tauri::WindowEvent::Focused(false) = event {
if let Some(window) = app.get_webview_window("main") { return;
let _ = window.hide();
}
} }
}) })
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![