快速面板模块
This commit is contained in:
@@ -0,0 +1,320 @@
|
||||
//! Tauri 命令:快速面板模块
|
||||
|
||||
use tauri::AppHandle;
|
||||
|
||||
use super::popup::{self, QuickPanelSettings};
|
||||
use super::{file_index, app_scanner, icon_extractor};
|
||||
|
||||
/// 读取快速面板设置(快捷键等)
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_get_settings(app: AppHandle) -> Result<QuickPanelSettings, String> {
|
||||
Ok(popup::load_settings(&app))
|
||||
}
|
||||
|
||||
/// 保存快速面板设置;快捷键变化时自动重新注册 + 预创建窗口
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_save_settings(
|
||||
settings: QuickPanelSettings,
|
||||
app: AppHandle,
|
||||
) -> Result<(), String> {
|
||||
let prev_shortcut = popup::load_settings(&app).shortcut;
|
||||
popup::save_settings(&app, &settings)?;
|
||||
// 快捷键变化时重新注册
|
||||
if settings.shortcut != prev_shortcut {
|
||||
popup::register_shortcut(&app, &settings.shortcut)?;
|
||||
// 新快捷键非空时确保弹窗窗口已预创建
|
||||
if !settings.shortcut.trim().is_empty() {
|
||||
popup::ensure_window(&app);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 注册(或切换)快速面板全局快捷键
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_register_shortcut(
|
||||
shortcut: String,
|
||||
app: AppHandle,
|
||||
) -> Result<(), String> {
|
||||
popup::register_shortcut(&app, &shortcut)
|
||||
}
|
||||
|
||||
/// 注销快速面板全局快捷键
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_unregister_shortcut(app: AppHandle) -> Result<(), String> {
|
||||
popup::unregister_shortcut(&app);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 手动触发显示快速面板(供 UI 按钮调用)
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_show_popup(app: AppHandle) -> Result<(), String> {
|
||||
popup::show_popup(&app);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 隐藏快速面板
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_hide_popup(app: AppHandle) -> Result<(), String> {
|
||||
popup::hide_popup(&app);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 显示已创建的弹窗窗口(前端 onMounted 后调用)
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_show_window(app: AppHandle) -> Result<(), String> {
|
||||
popup::show_window(&app);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 锁定屏幕(Windows: rundll32 user32.dll,LockWorkStation,CREATE_NO_WINDOW 避免黑窗)
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_lock_screen() -> Result<(), String> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use crate::process_manager::setup_creation_flags;
|
||||
let mut cmd = std::process::Command::new("rundll32.exe");
|
||||
cmd.arg("user32.dll,LockWorkStation");
|
||||
setup_creation_flags(&mut cmd);
|
||||
cmd.spawn().map_err(|e| format!("锁屏失败: {}", e))?;
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
return Err("当前平台不支持锁屏".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 初始化文件索引数据库(应用启动时调用)
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_init_file_index(app: AppHandle) -> Result<(), String> {
|
||||
file_index::init(&app);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 构建文件索引(全量重建,阻塞操作建议在 spawn_blocking 调用)
|
||||
#[tauri::command]
|
||||
pub async fn quickpanel_build_file_index(app: AppHandle) -> Result<i64, String> {
|
||||
let settings = popup::load_settings(&app);
|
||||
let dirs = if settings.index_dirs.is_empty() {
|
||||
popup::QuickPanelSettings::default().index_dirs
|
||||
} else {
|
||||
settings.index_dirs
|
||||
};
|
||||
// 阻塞操作放到 spawn_blocking
|
||||
tauri::async_runtime::spawn_blocking(move || file_index::build_index(&dirs))
|
||||
.await
|
||||
.map_err(|e| format!("索引任务失败: {}", e))?
|
||||
}
|
||||
|
||||
/// 搜索文件索引
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_search_files(query: String, limit: Option<i64>) -> Vec<file_index::FileRecord> {
|
||||
file_index::search(&query, limit.unwrap_or(50))
|
||||
}
|
||||
|
||||
/// 获取索引状态
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_file_index_stats() -> file_index::IndexStats {
|
||||
file_index::stats()
|
||||
}
|
||||
|
||||
/// 扫描已安装应用
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_scan_apps() -> Vec<app_scanner::AppRecord> {
|
||||
app_scanner::scan_apps()
|
||||
}
|
||||
|
||||
/// 获取应用图标(data URL)。命中内存/磁盘缓存时零 Windows API 调用。
|
||||
/// 前端按需为可见项调用,避免一次性加载全部图标。
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_get_app_icon(app: AppHandle, path: String) -> Option<String> {
|
||||
icon_extractor::get_icon_data_url(&app, &path)
|
||||
}
|
||||
|
||||
/// 清理图标缓存(磁盘 + 内存)
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_clear_app_icon_cache(app: AppHandle) -> Result<(), String> {
|
||||
icon_extractor::clear_cache(&app);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 在资源管理器中显示文件(选中)
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_reveal_in_explorer(path: String) -> Result<(), String> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use windows_sys::Win32::Foundation::HWND;
|
||||
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
||||
|
||||
// 规范化路径分隔符
|
||||
let normalized = path.replace('/', "\\");
|
||||
// explorer.exe /select,"path" — 用 ShellExecuteW 直接传参,
|
||||
// 避免 std::process::Command 的 arg 转义破坏 /select 语法。
|
||||
// 对 .lnk 文件也能正确选中(explorer 直接选中 .lnk 文件本身)。
|
||||
let params = format!("/select,\"{}\"", normalized);
|
||||
let wide_exe: Vec<u16> = OsStr::new("explorer.exe")
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let wide_params: Vec<u16> = OsStr::new(¶ms)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
let hinst = ShellExecuteW(
|
||||
0 as HWND,
|
||||
std::ptr::null(),
|
||||
wide_exe.as_ptr(),
|
||||
wide_params.as_ptr(),
|
||||
std::ptr::null(),
|
||||
SW_SHOWNORMAL,
|
||||
);
|
||||
// ShellExecuteW 返回值 <= 32 表示错误
|
||||
if hinst <= 32 {
|
||||
return Err(format!("打开资源管理器失败 (code: {})", hinst as i32));
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let _ = path;
|
||||
return Err("当前平台不支持".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 用系统默认程序打开文件(ShellExecuteW)。
|
||||
/// 无关联应用时,自动 fallback 到「打开方式」对话框(verb: openas)。
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_open_file(path: String) -> Result<(), String> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use windows_sys::Win32::Foundation::HWND;
|
||||
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
||||
|
||||
let normalized = path.replace('/', "\\");
|
||||
let wide_path: Vec<u16> = OsStr::new(&normalized)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let wide_open: Vec<u16> = OsStr::new("open")
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let wide_openas: Vec<u16> = OsStr::new("openas")
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
let mut hinst = ShellExecuteW(
|
||||
0 as HWND,
|
||||
wide_open.as_ptr(),
|
||||
wide_path.as_ptr(),
|
||||
std::ptr::null(),
|
||||
std::ptr::null(),
|
||||
SW_SHOWNORMAL,
|
||||
);
|
||||
if hinst <= 32 {
|
||||
hinst = ShellExecuteW(
|
||||
0 as HWND,
|
||||
wide_openas.as_ptr(),
|
||||
wide_path.as_ptr(),
|
||||
std::ptr::null(),
|
||||
std::ptr::null(),
|
||||
SW_SHOWNORMAL,
|
||||
);
|
||||
if hinst <= 32 {
|
||||
return Err(format!("打开文件失败 (code: {})", hinst as i32));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let _ = path;
|
||||
return Err(String::from("当前平台不支持"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 删除文件(移到回收站)
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_delete_file(path: String) -> Result<(), String> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use crate::process_manager::setup_creation_flags;
|
||||
let p = std::path::Path::new(&path);
|
||||
let is_dir = p.is_dir();
|
||||
// 用 PowerShell + Microsoft.VisualBasic 移到回收站
|
||||
let script = if is_dir {
|
||||
format!(
|
||||
"Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory('{}','OnlyErrorDialogs','SendToRecycleBin')",
|
||||
path.replace('\'', "''")
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('{}','OnlyErrorDialogs','SendToRecycleBin')",
|
||||
path.replace('\'', "''")
|
||||
)
|
||||
};
|
||||
let mut cmd = std::process::Command::new("powershell.exe");
|
||||
cmd.args(["-NoProfile", "-NonInteractive", "-Command", &script]);
|
||||
setup_creation_flags(&mut cmd);
|
||||
let output = cmd.output().map_err(|e| format!("删除失败: {}", e))?;
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"删除失败: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let _ = path;
|
||||
return Err("当前平台不支持".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 运行自定义命令(执行可执行文件 + 参数)
|
||||
/// .lnk 快捷方式不能直接 spawn(os error 193),需通过 cmd /C 启动
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_run_custom_command(command: String, args: Vec<String>) -> Result<(), String> {
|
||||
use crate::process_manager::setup_creation_flags;
|
||||
let is_lnk = command
|
||||
.to_lowercase()
|
||||
.ends_with(".lnk");
|
||||
let mut cmd = if is_lnk {
|
||||
// cmd /C start "" "path.lnk" arg1 arg2
|
||||
let mut c = std::process::Command::new("cmd");
|
||||
c.args(["/C", "start", "", &command]);
|
||||
c.args(&args);
|
||||
c
|
||||
} else {
|
||||
let mut c = std::process::Command::new(&command);
|
||||
c.args(&args);
|
||||
c
|
||||
};
|
||||
setup_creation_flags(&mut cmd);
|
||||
cmd.spawn().map_err(|e| format!("运行命令失败: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 运行系统命令(不设置 CREATE_NO_WINDOW,使 cmd/powershell/regedit 等显示自身窗口)
|
||||
/// 适用于内置系统工具:regedit、shutdown、cmd、powershell、taskmgr 等。
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_run_system_command(command: String, args: Vec<String>) -> Result<(), String> {
|
||||
let mut cmd = std::process::Command::new(&command);
|
||||
cmd.args(&args);
|
||||
cmd.spawn().map_err(|e| format!("运行系统命令失败: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user