截图模块优化调整

This commit is contained in:
zhongluofeng
2026-08-03 17:36:07 +08:00
parent f51a7f894e
commit c913379e2b
19 changed files with 1744 additions and 300 deletions
+4
View File
@@ -134,6 +134,10 @@ pub async fn clipboard_save_settings(
// 快捷键变化时重新注册
if settings.shortcut != prev_shortcut {
super::popup::register_shortcut(&app, &settings.shortcut)?;
// 新快捷键非空时确保弹窗窗口已预创建
if !settings.shortcut.trim().is_empty() {
super::popup::ensure_popup_window(&app);
}
}
Ok(())
}
+65 -39
View File
@@ -9,6 +9,7 @@
//! 6. 窗口失焦自动隐藏
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder, Emitter};
use tauri::window::{Effect, EffectsBuilder};
use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState};
@@ -19,6 +20,10 @@ pub const POPUP_LABEL: &str = "clipboard-popup";
/// 当前注册的快捷键(用于注销旧快捷键)
static CURRENT_SHORTCUT: Mutex<Option<String>> = Mutex::new(None);
/// 标志:show_popup 兜底创建路径设为 true,前端 onMounted 回调 show_window 时据此判断是否显示。
/// 预创建路径不设置,避免应用启动时弹窗自动弹出。
static POPUP_PENDING_SHOW: AtomicBool = AtomicBool::new(false);
/// 解析快捷键字符串为 Shortcut(格式如 "Alt+V"、"Ctrl+Shift+V"
/// 失败返回 None。
pub fn parse_shortcut(s: &str) -> Option<Shortcut> {
@@ -69,6 +74,58 @@ pub fn unregister_shortcut(app: &AppHandle) {
}
}
/// 创建弹窗窗口(隐藏状态)并注册失焦监听。
/// 位置默认在屏幕外,show_popup 时会重新定位。
/// 预创建后首次按快捷键走"窗口已存在"分支直接 show,避免首次创建的时序问题。
fn create_popup_window(app: &AppHandle) {
let win = match WebviewWindowBuilder::new(
app,
POPUP_LABEL,
WebviewUrl::App("index.html#clipboard-popup".into()),
)
.title("剪贴板")
.inner_size(380.0, 460.0)
.position(-10000.0, -10000.0) // 屏幕外,避免隐藏时一闪
.decorations(false)
.transparent(true)
.shadow(true)
.always_on_top(true)
.skip_taskbar(true)
.resizable(false)
.visible(false)
.focused(false) // 不抢占焦点,避免创建即触发 Focused(false)
.effects(EffectsBuilder::new().effects(vec![Effect::Mica]).build())
.build()
{
Ok(w) => w,
Err(e) => {
eprintln!("[clipboard] 创建弹窗失败: {}", e);
return;
}
};
// 监听窗口失焦:自动隐藏
let app_handle = app.clone();
let win_handle = win.clone();
win.on_window_event(move |event| {
if let tauri::WindowEvent::Focused(false) = event {
let _ = win_handle.hide();
let _ = app_handle.emit("clipboard-popup-hide", ());
}
});
eprintln!("[clipboard] 弹窗窗口已预创建(隐藏状态)");
}
/// 应用启动时预创建弹窗窗口(隐藏)。
/// 这样首次按快捷键时窗口已存在,直接 show + 定位,避免首次创建时序问题。
pub fn ensure_popup_window(app: &AppHandle) {
if app.get_webview_window(POPUP_LABEL).is_some() {
return;
}
create_popup_window(app);
}
/// 在鼠标当前位置显示弹窗。
/// 窗口不存在则创建(隐藏状态,等前端挂载后调用 show_window 显示)。
/// 窗口已存在则移动到鼠标位置并显示。
@@ -115,49 +172,18 @@ pub fn show_popup(app: &AppHandle) {
return;
}
// 首次创建窗口:先隐藏,等前端挂载完成后再显示
// 这样确保主题效果(dark class、mica)在窗口可见前已应用
let win = match WebviewWindowBuilder::new(
app,
POPUP_LABEL,
WebviewUrl::App("index.html#clipboard-popup".into()),
)
.title("剪贴板")
.inner_size(w, h)
.position(x, y)
.decorations(false)
.transparent(true)
.shadow(true)
.always_on_top(true)
.skip_taskbar(true)
.resizable(false)
.visible(false) // 关键:先隐藏,前端 onMounted 后调用 show_window
.focused(true)
.effects(EffectsBuilder::new().effects(vec![Effect::Mica]).build())
.build()
{
Ok(w) => w,
Err(e) => {
eprintln!("[clipboard] 创建弹窗失败: {}", e);
return;
}
};
// 监听窗口失焦:自动隐藏
let app_handle = app.clone();
let win_handle = win.clone();
win.on_window_event(move |event| {
if let tauri::WindowEvent::Focused(false) = event {
let _ = win_handle.hide();
let _ = app_handle.emit("clipboard-popup-hide", ());
}
});
eprintln!("[clipboard] 弹窗窗口已创建(隐藏状态)");
// 兜底:窗口被销毁时重新创建(隐藏,等前端 onMounted 回调 show_window
POPUP_PENDING_SHOW.store(true, Ordering::SeqCst);
create_popup_window(app);
}
/// 显示已创建的弹窗窗口(由前端 onMounted 后调用)。
/// 预创建路径下前端 onMounted 也会调用此函数,但 POPUP_PENDING_SHOW 为 false 时直接跳过,
/// 避免应用启动时弹窗自动弹出。仅 show_popup 兜底创建路径才真正显示。
pub fn show_window(app: &AppHandle) {
if !POPUP_PENDING_SHOW.swap(false, Ordering::SeqCst) {
return;
}
if let Some(win) = app.get_webview_window(POPUP_LABEL) {
let _ = win.show();
let _ = win.set_focus();