From d743b208d6624729e421aeec7f760feb570ee838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B4=BE=E4=BA=86=E4=B8=AA=E8=90=8C?= Date: Sun, 19 Mar 2023 22:11:34 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dwindows=20=E9=AB=98?= =?UTF-8?q?=E5=88=86=E5=B1=8F=E7=AA=97=E5=8F=A3=E8=BF=87=E5=B0=8F=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/Cargo.toml | 2 +- src-tauri/src/main.rs | 1 + src-tauri/src/selection.rs | 37 +++++- src-tauri/src/shortcut.rs | 226 +------------------------------------ src-tauri/src/window.rs | 184 ++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 230 deletions(-) create mode 100644 src-tauri/src/window.rs diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index df9caf1a2f..450b7fa563 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,7 +22,7 @@ toml = "0.7.3" [target.'cfg(windows)'.dependencies] window-shadows = "0.2" -windows = {version="0.44.0",features= ["Win32_UI_WindowsAndMessaging", "Win32_Foundation"] } +windows = {version="0.44.0",features= ["Win32_UI_WindowsAndMessaging", "Win32_Foundation","Win32_UI_HiDpi"] } enigo = {git = "https://github.com/enigo-rs/enigo"} cli-clipboard = "0.4.0" diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 358277465b..a0690d19b2 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,6 +7,7 @@ mod config; mod selection; mod shortcut; mod trayicon; +mod window; use config::*; use once_cell::sync::OnceCell; diff --git a/src-tauri/src/selection.rs b/src-tauri/src/selection.rs index e47a8d38e3..216c4620f6 100644 --- a/src-tauri/src/selection.rs +++ b/src-tauri/src/selection.rs @@ -9,8 +9,8 @@ pub fn get_selection_text() -> Result { }; } -// 获取选择的文本(Windows) -#[cfg(target_os = "windows")] +// 获取选择的文本(Windows,MacOS) +#[cfg(any(target_os = "windows", target_os = "macos"))] #[tauri::command] pub fn get_selection_text() -> Result { use cli_clipboard::{ClipboardContext, ClipboardProvider}; @@ -21,9 +21,34 @@ pub fn get_selection_text() -> Result { } } -// 获取选择的文本(MacOS) +// macos 复制操作 #[cfg(target_os = "macos")] -#[tauri::command] -pub fn get_selection_text() -> Result { - Ok("ToDo".to_string()) +pub fn copy() { + use enigo::*; + let mut enigo = Enigo::new(); + // 先释放按键 + enigo.key_up(Key::Command); + enigo.key_up(Key::Alt); + enigo.key_up(Key::Shift); + enigo.key_up(Key::Space); + // 发送CtrlC + enigo.key_down(Key::Command); + enigo.key_click(Key::Layout('c')); + enigo.key_up(Key::Command); +} + +// windows 复制操作 +#[cfg(target_os = "windows")] +pub fn copy() { + use enigo::*; + let mut enigo = Enigo::new(); + // 先释放按键 + enigo.key_up(Key::Control); + enigo.key_up(Key::Alt); + enigo.key_up(Key::Shift); + enigo.key_up(Key::Space); + // 发送CtrlC + enigo.key_down(Key::Control); + enigo.key_click(Key::Layout('c')); + enigo.key_up(Key::Control); } diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs index 3db8098c7e..3e8aeb0d99 100644 --- a/src-tauri/src/shortcut.rs +++ b/src-tauri/src/shortcut.rs @@ -1,227 +1,7 @@ -use crate::config::get_config; use crate::APP; -#[cfg(target_os = "linux")] -use tauri::WindowEvent; -#[cfg(target_os = "macos")] -use tauri::WindowEvent; -use tauri::{GlobalShortcutManager, Manager, PhysicalPosition, PhysicalSize}; +use crate::{config::get_config, window::persistent_window, window::translate_window}; +use tauri::{GlobalShortcutManager, Manager}; use toml::Value; -#[cfg(target_os = "windows")] -use window_shadows::set_shadow; -#[cfg(target_os = "macos")] -use window_shadows::set_shadow; - -// 失去焦点自动关闭窗口 -// Gnome 下存在焦点捕获失败bug,windows下拖动窗口会失去焦点 -#[cfg(all(unix))] -fn on_lose_focus(event: &WindowEvent) { - match event { - WindowEvent::Focused(v) => { - if !v { - let handle = APP.get().unwrap(); - match handle.get_window("translator") { - Some(window) => { - window.close().unwrap(); - } - None => {} - } - } - } - _ => {} - } -} - -// windows 复制操作 -#[cfg(target_os = "windows")] -fn copy() { - use enigo::*; - let mut enigo = Enigo::new(); - // 先释放按键 - enigo.key_up(Key::Control); - enigo.key_up(Key::Alt); - enigo.key_up(Key::Shift); - enigo.key_up(Key::Space); - // 发送CtrlC - enigo.key_down(Key::Control); - enigo.key_click(Key::Layout('c')); - enigo.key_up(Key::Control); -} -// 获取鼠标坐标 -#[cfg(target_os = "linux")] -fn get_mouse_location() -> Result<(i32, i32), String> { - use std::process::Command; - let output: String = match Command::new("xdotool").arg("getmouselocation").output() { - Ok(v) => String::from_utf8(v.stdout).unwrap(), - Err(e) => return Err(format!("xsel执行出错{}", e.to_string())), - }; - let output: Vec<&str> = output.split_whitespace().collect(); - let x = output - .get(0) - .unwrap() - .replace("x:", "") - .parse::() - .unwrap(); - let y = output - .get(1) - .unwrap() - .replace("y:", "") - .parse::() - .unwrap(); - return Ok((x, y)); -} -#[cfg(target_os = "windows")] -fn get_mouse_location() -> Result<(i32, i32), String> { - use windows::Win32::Foundation::POINT; - use windows::Win32::Foundation::RECT; - use windows::Win32::UI::WindowsAndMessaging::GetWindowRect; - use windows::Win32::UI::WindowsAndMessaging::{GetCursorPos, GetDesktopWindow}; - let mut point = POINT { x: 0, y: 0 }; - let mut rect = RECT { - left: 0, - top: 0, - right: 0, - bottom: 0, - }; - unsafe { - if GetCursorPos(&mut point).as_bool() { - let mut x = point.x; - let mut y = point.y; - // 获取桌面窗口的句柄 - let hwnd = GetDesktopWindow(); - if GetWindowRect(hwnd, &mut rect).as_bool() { - if point.x + 400 > rect.right { - x = rect.right - 400; - } - if point.y + 500 > rect.bottom { - y = rect.bottom - 500; - } - } - return Ok((x, y)); - } else { - return Err("error".to_string()); - } - } -} -// 划词翻译 -fn translate() { - #[cfg(target_os = "windows")] - let (x, y) = get_mouse_location().unwrap(); - #[cfg(target_os = "linux")] - let (x, y) = get_mouse_location().unwrap(); - // 复制操作必须在拉起窗口之前,否则焦点会丢失 - #[cfg(target_os = "windows")] - copy(); - let handle = APP.get().unwrap(); - match handle.get_window("translator") { - Some(window) => { - window.close().unwrap(); - } - None => { - let builder = tauri::WindowBuilder::new( - handle, - "translator", - tauri::WindowUrl::App("index_translator.html".into()), - ) - .always_on_top(true) - .decorations(false) - .skip_taskbar(true) - .center() - .focused(true) - .title("Translator"); - - #[cfg(target_os = "macos")] - { - let window = builder.build().unwrap(); - window.set_size(PhysicalSize::new(400, 500)).unwrap(); - window - .set_min_size(Some(PhysicalSize::new(400, 400))) - .unwrap(); - set_shadow(&window, true).unwrap_or_default(); - - window.on_window_event(on_lose_focus); - } - - #[cfg(target_os = "windows")] - { - let window = builder.build().unwrap(); - window.set_size(PhysicalSize::new(400, 500)).unwrap(); - window - .set_min_size(Some(PhysicalSize::new(400, 400))) - .unwrap(); - set_shadow(&window, true).unwrap_or_default(); - - window.set_position(PhysicalPosition::new(x, y)).unwrap(); - } - #[cfg(target_os = "linux")] - { - let window = builder - .transparent(true) - // x11根据inner_size这个初始值来判断窗口是否超出屏幕 - // 不设置的话会有一个默认尺寸,导致窗口出现位置不对 - .inner_size(400.0, 500.0) - .build() - .unwrap(); - window.set_size(PhysicalSize::new(400, 500)).unwrap(); - window - .set_min_size(Some(PhysicalSize::new(400, 400))) - .unwrap(); - // Windows 下拖动窗口会失去焦点,此方法不适用 - window.on_window_event(on_lose_focus); - window.set_position(PhysicalPosition::new(x, y)).unwrap(); - } - } - }; -} - -// 持久窗口 -fn persistent_window() { - let handle = APP.get().unwrap(); - match handle.get_window("persistent") { - Some(window) => { - window.close().unwrap(); - } - None => { - let builder = tauri::WindowBuilder::new( - handle, - "persistent", - tauri::WindowUrl::App("index_translator.html".into()), - ) - .always_on_top(true) - .decorations(false) - .center() - .title("Translator"); - - #[cfg(target_os = "macos")] - { - let window = builder.build().unwrap(); - window.set_size(PhysicalSize::new(400, 500)).unwrap(); - window - .set_min_size(Some(PhysicalSize::new(400, 400))) - .unwrap(); - set_shadow(&window, true).unwrap_or_default(); - } - - #[cfg(target_os = "windows")] - { - let window = builder.build().unwrap(); - window.set_size(PhysicalSize::new(400, 500)).unwrap(); - window - .set_min_size(Some(PhysicalSize::new(400, 400))) - .unwrap(); - set_shadow(&window, true).unwrap_or_default(); - } - - #[cfg(target_os = "linux")] - { - let window = builder.transparent(true).build().unwrap(); - window.set_size(PhysicalSize::new(400, 500)).unwrap(); - window - .set_min_size(Some(PhysicalSize::new(400, 400))) - .unwrap(); - } - } - }; -} // 注册全局快捷键 pub fn register_shortcut() -> Result<(), String> { @@ -237,7 +17,7 @@ pub fn register_shortcut() -> Result<(), String> { if shortcut_translate.as_str().unwrap() != "" { match handle .global_shortcut_manager() - .register(shortcut_translate.as_str().unwrap(), translate) + .register(shortcut_translate.as_str().unwrap(), translate_window) { Ok(()) => {} Err(e) => return Err(e.to_string()), diff --git a/src-tauri/src/window.rs b/src-tauri/src/window.rs new file mode 100644 index 0000000000..d701d5ecc0 --- /dev/null +++ b/src-tauri/src/window.rs @@ -0,0 +1,184 @@ +use crate::APP; +#[cfg(any(target_os = "macos", target_os = "linux"))] +use tauri::WindowEvent; +use tauri::{Manager, PhysicalPosition}; +#[cfg(any(target_os = "macos", target_os = "windows"))] +use window_shadows::set_shadow; + +// 后续从设置读取 +const WIDTH: f64 = 400.0; +const HEIGHT: f64 = 500.0; +// 失去焦点自动关闭窗口 +// Gnome 下存在焦点捕获失败bug,windows下拖动窗口会失去焦点 +#[cfg(any(target_os = "macos", target_os = "linux"))] +fn on_lose_focus(event: &WindowEvent) { + match event { + WindowEvent::Focused(v) => { + if !v { + let handle = APP.get().unwrap(); + match handle.get_window("translator") { + Some(window) => { + window.close().unwrap(); + } + None => {} + } + } + } + _ => {} + } +} + +// 获取鼠标坐标 +#[cfg(target_os = "linux")] +fn get_mouse_location() -> Result<(i32, i32), String> { + use std::process::Command; + let output: String = match Command::new("xdotool").arg("getmouselocation").output() { + Ok(v) => String::from_utf8(v.stdout).unwrap(), + Err(e) => return Err(format!("xdotool执行出错{}", e.to_string())), + }; + let output: Vec<&str> = output.split_whitespace().collect(); + let x = output + .get(0) + .unwrap() + .replace("x:", "") + .parse::() + .unwrap(); + let y = output + .get(1) + .unwrap() + .replace("y:", "") + .parse::() + .unwrap(); + return Ok((x, y)); +} + +#[cfg(target_os = "windows")] +fn get_mouse_location() -> Result<(i32, i32), String> { + use windows::Win32::Foundation::POINT; + use windows::Win32::Foundation::RECT; + use windows::Win32::UI::HiDpi::GetDpiForWindow; + use windows::Win32::UI::WindowsAndMessaging::GetWindowRect; + use windows::Win32::UI::WindowsAndMessaging::{GetCursorPos, GetDesktopWindow}; + let mut point = POINT { x: 0, y: 0 }; + let mut rect = RECT { + left: 0, + top: 0, + right: 0, + bottom: 0, + }; + + unsafe { + if GetCursorPos(&mut point).as_bool() { + let mut x = point.x as f64; + let mut y = point.y as f64; + // 获取桌面窗口的句柄 + let hwnd = GetDesktopWindow(); + let dpi = GetDpiForWindow(hwnd) as f64; + if GetWindowRect(hwnd, &mut rect).as_bool() { + // 由于获取到的屏幕大小以及鼠标坐标为物理像素,所以需要转换 + if point.x as f64 + WIDTH * (dpi / 100.0) > (rect.right - rect.left) as f64 { + x = (rect.right - rect.left) as f64 - WIDTH * (dpi / 100.0); + } + if point.y as f64 + HEIGHT * (dpi / 100.0) > (rect.bottom - rect.top) as f64 { + y = (rect.bottom - rect.top) as f64 - HEIGHT * (dpi / 100.0); + } + } + return Ok((x as i32, y as i32)); + } else { + return Err("error".to_string()); + } + } +} +// 划词翻译 +pub fn translate_window() { + #[cfg(any(target_os = "windows", target_os = "macos"))] + { + // 复制操作必须在拉起窗口之前,否则焦点会丢失 + use crate::selection::copy; + copy(); + } + + #[cfg(any(target_os = "windows", target_os = "linux"))] + let (x, y) = get_mouse_location().unwrap(); + + let handle = APP.get().unwrap(); + match handle.get_window("translator") { + Some(window) => { + window.close().unwrap(); + } + None => { + let builder = tauri::WindowBuilder::new( + handle, + "translator", + tauri::WindowUrl::App("index_translator.html".into()), + ) + .inner_size(WIDTH, HEIGHT) + .always_on_top(true) + .decorations(false) + .skip_taskbar(true) + .center() + .focused(true) + .title("Translator"); + + #[cfg(target_os = "macos")] + { + let window = builder.build().unwrap(); + set_shadow(&window, true).unwrap_or_default(); + window.on_window_event(on_lose_focus); + } + + #[cfg(target_os = "windows")] + { + let window = builder.build().unwrap(); + set_shadow(&window, true).unwrap_or_default(); + window.set_position(PhysicalPosition::new(x, y)).unwrap(); + } + + #[cfg(target_os = "linux")] + { + let window = builder.transparent(true).build().unwrap(); + window.on_window_event(on_lose_focus); + window.set_position(PhysicalPosition::new(x, y)).unwrap(); + } + } + }; +} + +// 持久窗口 +pub fn persistent_window() { + let handle = APP.get().unwrap(); + match handle.get_window("persistent") { + Some(window) => { + window.close().unwrap(); + } + None => { + let builder = tauri::WindowBuilder::new( + handle, + "persistent", + tauri::WindowUrl::App("index_translator.html".into()), + ) + .inner_size(WIDTH, HEIGHT) + .always_on_top(true) + .decorations(false) + .center() + .title("Translator"); + + #[cfg(target_os = "macos")] + { + let window = builder.build().unwrap(); + set_shadow(&window, true).unwrap_or_default(); + } + + #[cfg(target_os = "windows")] + { + let window = builder.build().unwrap(); + set_shadow(&window, true).unwrap_or_default(); + } + + #[cfg(target_os = "linux")] + { + let window = builder.transparent(true).build().unwrap(); + } + } + }; +}