快速面板模块
This commit is contained in:
@@ -65,10 +65,11 @@ use clipboard::{
|
||||
use quickpanel::{
|
||||
quickpanel_build_file_index, quickpanel_clear_app_icon_cache, quickpanel_delete_file,
|
||||
quickpanel_file_index_stats, quickpanel_get_app_icon, quickpanel_get_settings,
|
||||
quickpanel_hide_popup, quickpanel_init_file_index, quickpanel_lock_screen,
|
||||
quickpanel_open_file, quickpanel_register_shortcut, quickpanel_reveal_in_explorer,
|
||||
quickpanel_run_custom_command, quickpanel_run_system_command, quickpanel_save_settings,
|
||||
quickpanel_scan_apps, quickpanel_search_files, quickpanel_show_popup, quickpanel_show_window,
|
||||
quickpanel_get_special_locations, quickpanel_hide_popup, quickpanel_init_file_index,
|
||||
quickpanel_lock_screen, quickpanel_open_file, quickpanel_open_special,
|
||||
quickpanel_register_shortcut, quickpanel_reveal_in_explorer, quickpanel_run_custom_command,
|
||||
quickpanel_run_system_command, quickpanel_save_settings, quickpanel_scan_apps,
|
||||
quickpanel_search_files, quickpanel_show_popup, quickpanel_show_window,
|
||||
quickpanel_unregister_shortcut,
|
||||
};
|
||||
use tray_menu::{tray_menu_action, tray_menu_hide, tray_menu_ready};
|
||||
@@ -225,6 +226,8 @@ pub fn run() {
|
||||
quickpanel_delete_file,
|
||||
quickpanel_run_custom_command,
|
||||
quickpanel_run_system_command,
|
||||
quickpanel_get_special_locations,
|
||||
quickpanel_open_special,
|
||||
tray_menu_action,
|
||||
tray_menu_hide,
|
||||
tray_menu_ready,
|
||||
|
||||
@@ -188,62 +188,32 @@ pub fn quickpanel_reveal_in_explorer(path: String) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 用系统默认程序打开文件(ShellExecuteW)。
|
||||
/// 无关联应用时,自动 fallback 到「打开方式」对话框(verb: openas)。
|
||||
/// 用系统默认程序打开文件/文件夹。
|
||||
/// - 目录:explorer.exe 直接打开(修复索引目录点击后未打开的问题)
|
||||
/// - 文件:ShellExecuteW open,无关联应用时自动 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;
|
||||
super::special_locations::open_path(&path)
|
||||
}
|
||||
|
||||
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();
|
||||
/// 获取 Windows 常用快捷位置(hosts、回收站、此电脑、用户目录、系统管理工具等)
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_get_special_locations() -> Vec<super::special_locations::SpecialLocation> {
|
||||
super::special_locations::get_special_locations()
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 打开快捷位置(kind: file | shell | cmd)
|
||||
#[tauri::command]
|
||||
pub fn quickpanel_open_special(
|
||||
kind: String,
|
||||
target: String,
|
||||
args: Vec<String>,
|
||||
) -> Result<(), String> {
|
||||
match kind.as_str() {
|
||||
"shell" => super::special_locations::open_shell(&target),
|
||||
"cmd" => super::special_locations::open_cmd(&target, &args),
|
||||
_ => super::special_locations::open_path(&target),
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let _ = path;
|
||||
return Err(String::from("当前平台不支持"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 删除文件(移到回收站)
|
||||
|
||||
@@ -9,14 +9,16 @@ pub mod commands;
|
||||
pub mod file_index;
|
||||
pub mod icon_extractor;
|
||||
pub mod popup;
|
||||
pub mod special_locations;
|
||||
|
||||
pub use commands::{
|
||||
quickpanel_build_file_index, quickpanel_clear_app_icon_cache, quickpanel_delete_file,
|
||||
quickpanel_file_index_stats, quickpanel_get_app_icon, quickpanel_get_settings,
|
||||
quickpanel_hide_popup, quickpanel_init_file_index, quickpanel_lock_screen,
|
||||
quickpanel_open_file, quickpanel_register_shortcut, quickpanel_reveal_in_explorer,
|
||||
quickpanel_run_custom_command, quickpanel_run_system_command, quickpanel_save_settings,
|
||||
quickpanel_scan_apps, quickpanel_search_files, quickpanel_show_popup, quickpanel_show_window,
|
||||
quickpanel_get_special_locations, quickpanel_hide_popup, quickpanel_init_file_index,
|
||||
quickpanel_lock_screen, quickpanel_open_file, quickpanel_open_special,
|
||||
quickpanel_register_shortcut, quickpanel_reveal_in_explorer, quickpanel_run_custom_command,
|
||||
quickpanel_run_system_command, quickpanel_save_settings, quickpanel_scan_apps,
|
||||
quickpanel_search_files, quickpanel_show_popup, quickpanel_show_window,
|
||||
quickpanel_unregister_shortcut,
|
||||
};
|
||||
pub use popup::{ensure_window, load_settings, register_shortcut};
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
//! 快捷位置:Windows 常用系统位置/工具。
|
||||
//!
|
||||
//! 提供两类能力:
|
||||
//! - `get_special_locations`:返回预定义列表(hosts、回收站、此电脑、用户目录、系统管理工具),
|
||||
//! 路径在 Rust 侧解析(环境变量 / dirs crate),前端只负责展示与搜索。
|
||||
//! - 打开逻辑:目录走 explorer.exe(比 ShellExecute "open" 目录更可靠),
|
||||
//! 文件走 ShellExecuteW(open + openas fallback),shell 位置与系统工具走 ShellExecuteW。
|
||||
|
||||
use std::path::PathBuf;
|
||||
use serde::Serialize;
|
||||
|
||||
/// 快捷位置条目
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SpecialLocation {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub subtitle: String,
|
||||
pub keywords: Vec<String>,
|
||||
/// file: 真实文件/文件夹路径;shell: explorer 打开的 shell 路径;cmd: 可执行命令
|
||||
pub kind: String,
|
||||
pub target: String,
|
||||
pub args: Vec<String>,
|
||||
}
|
||||
|
||||
fn sl(
|
||||
id: &str,
|
||||
title: &str,
|
||||
subtitle: &str,
|
||||
keywords: &[&str],
|
||||
kind: &str,
|
||||
target: &str,
|
||||
args: &[&str],
|
||||
) -> SpecialLocation {
|
||||
SpecialLocation {
|
||||
id: id.to_string(),
|
||||
title: title.to_string(),
|
||||
subtitle: subtitle.to_string(),
|
||||
keywords: keywords.iter().map(|s| s.to_string()).collect(),
|
||||
kind: kind.to_string(),
|
||||
target: target.to_string(),
|
||||
args: args.iter().map(|s| s.to_string()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Windows 系统目录(%SystemRoot%)
|
||||
fn system_root() -> String {
|
||||
std::env::var("SystemRoot")
|
||||
.or_else(|_| std::env::var("windir"))
|
||||
.unwrap_or_else(|_| "C:\\Windows".to_string())
|
||||
}
|
||||
|
||||
fn dir_str(d: Option<PathBuf>) -> Option<String> {
|
||||
d.map(|p| p.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
/// 返回全部快捷位置(路径已在 Rust 侧解析,避免前端硬编码)
|
||||
pub fn get_special_locations() -> Vec<SpecialLocation> {
|
||||
let mut list = Vec::new();
|
||||
|
||||
// ===== shell 位置(explorer 打开,CLSID 格式) =====
|
||||
list.push(sl(
|
||||
"sp-recycle-bin",
|
||||
"回收站",
|
||||
"Windows 回收站",
|
||||
&["recycle", "bin", "trash", "回收站", "垃圾桶", "垃圾箱"],
|
||||
"shell",
|
||||
"::{645FF040-5081-101B-9F08-00AA002F954E}",
|
||||
&[],
|
||||
));
|
||||
list.push(sl(
|
||||
"sp-this-pc",
|
||||
"此电脑",
|
||||
"我的电脑",
|
||||
&["此电脑", "我的电脑", "computer", "pc", "thispc"],
|
||||
"shell",
|
||||
"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
|
||||
&[],
|
||||
));
|
||||
list.push(sl(
|
||||
"sp-network",
|
||||
"网络",
|
||||
"网络位置",
|
||||
&["网络", "network", "网上邻居"],
|
||||
"shell",
|
||||
"::{208D2C60-3AEA-1069-A2D7-08002B30309D}",
|
||||
&[],
|
||||
));
|
||||
|
||||
// ===== 常用文件 =====
|
||||
let hosts = format!("{}\\System32\\drivers\\etc\\hosts", system_root());
|
||||
list.push(sl(
|
||||
"sp-hosts",
|
||||
"hosts 文件",
|
||||
&hosts,
|
||||
&["hosts", "host", "主机文件", "域名映射"],
|
||||
"file",
|
||||
&hosts,
|
||||
&[],
|
||||
));
|
||||
|
||||
// ===== 用户目录(真实路径) =====
|
||||
let user_dirs: &[(&str, &str, &[&str], Option<PathBuf>)] = &[
|
||||
("sp-documents", "我的文档", &["文档", "我的文档", "documents", "docs"], dirs::document_dir()),
|
||||
("sp-desktop", "桌面", &["桌面", "desktop"], dirs::desktop_dir()),
|
||||
("sp-downloads", "下载", &["下载", "download", "downloads"], dirs::download_dir()),
|
||||
("sp-pictures", "图片", &["图片", "照片", "pictures", "photos"], dirs::picture_dir()),
|
||||
("sp-music", "音乐", &["音乐", "music"], dirs::audio_dir()),
|
||||
("sp-videos", "视频", &["视频", "videos"], dirs::video_dir()),
|
||||
];
|
||||
for (id, title, keywords, path) in user_dirs {
|
||||
if let Some(p) = dir_str(path.clone()) {
|
||||
list.push(sl(id, title, &p, keywords, "file", &p, &[]));
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 系统管理工具(ShellExecuteW 直接启动 .msc/.cpl/.exe) =====
|
||||
let tools: &[(&str, &str, &[&str], &str)] = &[
|
||||
("sp-devmgmt", "设备管理器", &["设备管理器", "设备", "devmgmt", "device"], "devmgmt.msc"),
|
||||
("sp-diskmgmt", "磁盘管理", &["磁盘管理", "磁盘", "diskmgmt", "disk"], "diskmgmt.msc"),
|
||||
("sp-compmgmt", "计算机管理", &["计算机管理", "compmgmt"], "compmgmt.msc"),
|
||||
("sp-services", "服务", &["服务", "services", "service"], "services.msc"),
|
||||
("sp-perfmon", "性能监视器", &["性能监视器", "性能", "perfmon"], "perfmon.msc"),
|
||||
("sp-msinfo", "系统信息", &["系统信息", "msinfo", "systeminfo"], "msinfo32.exe"),
|
||||
("sp-ncpa", "网络连接", &["网络连接", "ncpa", "网卡"], "ncpa.cpl"),
|
||||
("sp-appwiz", "程序和功能", &["程序和功能", "卸载", "appwiz", "uninstall"], "appwiz.cpl"),
|
||||
("sp-powercfg", "电源选项", &["电源选项", "电源", "powercfg", "power"], "powercfg.cpl"),
|
||||
("sp-osk", "屏幕键盘", &["屏幕键盘", "键盘", "osk", "virtual keyboard"], "osk.exe"),
|
||||
("sp-magnify", "放大镜", &["放大镜", "magnify", "magnifier"], "magnify.exe"),
|
||||
];
|
||||
for (id, title, keywords, exe) in tools {
|
||||
list.push(sl(id, title, exe, keywords, "cmd", exe, &[]));
|
||||
}
|
||||
list.push(sl(
|
||||
"sp-envvar",
|
||||
"环境变量",
|
||||
"rundll32 sysdm.cpl,EditEnvironmentVariables",
|
||||
&["环境变量", "环境", "env", "environment"],
|
||||
"cmd",
|
||||
"rundll32.exe",
|
||||
&["sysdm.cpl,EditEnvironmentVariables"],
|
||||
));
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
// ===== 打开逻辑 =====
|
||||
|
||||
/// ShellExecuteW 调用(verb 可指定,如 "open" / "openas")
|
||||
#[cfg(windows)]
|
||||
fn shell_execute_verb(exe: &str, params: &str, verb: &str) -> Result<(), String> {
|
||||
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 wide_exe: Vec<u16> = OsStr::new(exe)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let wide_params: Vec<u16> = OsStr::new(params)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let wide_verb: Vec<u16> = OsStr::new(verb)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
let hinst = ShellExecuteW(
|
||||
0 as HWND,
|
||||
wide_verb.as_ptr(),
|
||||
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));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn shell_execute_verb(_exe: &str, _params: &str, _verb: &str) -> Result<(), String> {
|
||||
Err("当前平台不支持".into())
|
||||
}
|
||||
|
||||
/// 打开文件/文件夹路径:
|
||||
/// - 目录:explorer.exe 直接打开(比 ShellExecute "open" 目录更可靠,修复索引目录点击不生效问题)
|
||||
/// - 文件:ShellExecuteW open,无关联程序时 fallback 到「打开方式」对话框
|
||||
pub fn open_path(path: &str) -> Result<(), String> {
|
||||
let p = std::path::Path::new(path);
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if p.is_dir() {
|
||||
use crate::process_manager::setup_creation_flags;
|
||||
let mut cmd = std::process::Command::new("explorer.exe");
|
||||
cmd.arg(path);
|
||||
setup_creation_flags(&mut cmd);
|
||||
return cmd
|
||||
.spawn()
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("打开文件夹失败: {}", e));
|
||||
}
|
||||
let params = String::new();
|
||||
if shell_execute_verb(path, ¶ms, "open").is_err() {
|
||||
return shell_execute_verb(path, ¶ms, "openas");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let _ = p;
|
||||
Err("当前平台不支持".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// 打开 shell 位置(explorer shell: 或 ::{CLSID})
|
||||
pub fn open_shell(target: &str) -> Result<(), String> {
|
||||
shell_execute_verb("explorer.exe", target, "open")
|
||||
}
|
||||
|
||||
/// 打开系统工具(.msc / .cpl / .exe,参数空格连接)
|
||||
pub fn open_cmd(exe: &str, args: &[String]) -> Result<(), String> {
|
||||
let params = args.join(" ");
|
||||
shell_execute_verb(exe, ¶ms, "open")
|
||||
}
|
||||
Reference in New Issue
Block a user