This commit is contained in:
2026-07-02 00:47:03 +08:00
parent e4efa4b54d
commit 82a64787c3
12 changed files with 568 additions and 127 deletions

View File

@@ -1,4 +1,64 @@
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
use std::fs;
use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder};
use tauri::Emitter;
use tauri_plugin_dialog::DialogExt;
#[derive(Debug, Clone, serde::Serialize)]
struct DirEntry {
name: String,
path: String,
is_dir: bool,
}
#[tauri::command]
fn pick_folder(app: tauri::AppHandle) -> Option<String> {
app.dialog()
.file()
.blocking_pick_folder()
.map(|p| p.to_string())
}
#[tauri::command]
fn read_dir(path: String) -> Result<Vec<DirEntry>, String> {
let entries = fs::read_dir(&path).map_err(|e| format!("无法读取目录: {}", e))?;
let mut result = Vec::new();
for entry in entries {
let entry = entry.map_err(|e| format!("读取条目失败: {}", e))?;
let path = entry.path();
let name = entry
.file_name()
.to_string_lossy()
.to_string();
result.push(DirEntry {
name,
path: path.to_string_lossy().to_string(),
is_dir: path.is_dir(),
});
}
// Sort: directories first, then alphabetical
result.sort_by(|a, b| b.is_dir.cmp(&a.is_dir).then_with(|| a.name.cmp(&b.name)));
Ok(result)
}
#[tauri::command]
fn read_file(path: String) -> Result<String, String> {
fs::read_to_string(&path).map_err(|e| format!("无法读取文件: {}", e))
}
#[tauri::command]
fn write_file(path: String, content: String) -> Result<(), String> {
fs::write(&path, &content).map_err(|e| format!("无法保存文件: {}", e))
}
#[tauri::command]
fn pick_save_file(app: tauri::AppHandle) -> Option<String> {
app.dialog()
.file()
.blocking_save_file()
.map(|p| p.to_string())
}
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
@@ -8,7 +68,139 @@ fn greet(name: &str) -> String {
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![greet])
.plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![greet, pick_folder, read_dir, read_file, write_file, pick_save_file])
.setup(|app| {
// ---- File menu ----
let new = MenuItemBuilder::with_id("new", "新建")
.accelerator("CmdOrCtrl+N")
.build(app)?;
let open = MenuItemBuilder::with_id("open", "打开文件夹...")
.accelerator("CmdOrCtrl+O")
.build(app)?;
let save = MenuItemBuilder::with_id("save", "保存")
.accelerator("CmdOrCtrl+S")
.build(app)?;
let save_as = MenuItemBuilder::with_id("save_as", "另存为...")
.accelerator("CmdOrCtrl+Shift+S")
.build(app)?;
let quit = MenuItemBuilder::with_id("quit", "退出")
.accelerator("CmdOrCtrl+Q")
.build(app)?;
let file_menu = SubmenuBuilder::new(app, "文件")
.item(&new)
.item(&open)
.item(&save)
.item(&save_as)
.separator()
.item(&quit)
.build()?;
// ---- Edit menu ----
let undo = MenuItemBuilder::with_id("undo", "撤销")
.accelerator("CmdOrCtrl+Z")
.build(app)?;
let redo = MenuItemBuilder::with_id("redo", "重做")
.accelerator("CmdOrCtrl+Shift+Z")
.build(app)?;
let cut = MenuItemBuilder::with_id("cut", "剪切")
.accelerator("CmdOrCtrl+X")
.build(app)?;
let copy = MenuItemBuilder::with_id("copy", "复制")
.accelerator("CmdOrCtrl+C")
.build(app)?;
let paste = MenuItemBuilder::with_id("paste", "粘贴")
.accelerator("CmdOrCtrl+V")
.build(app)?;
let select_all = MenuItemBuilder::with_id("select_all", "全选")
.accelerator("CmdOrCtrl+A")
.build(app)?;
let edit_menu = SubmenuBuilder::new(app, "编辑")
.item(&undo)
.item(&redo)
.separator()
.item(&cut)
.item(&copy)
.item(&paste)
.separator()
.item(&select_all)
.build()?;
// ---- View menu ----
let toggle_outline = MenuItemBuilder::with_id("toggle_outline", "切换大纲")
.accelerator("CmdOrCtrl+B")
.build(app)?;
let view_menu = SubmenuBuilder::new(app, "视图")
.item(&toggle_outline)
.build()?;
// ---- Help menu ----
let about = MenuItemBuilder::with_id("about", "关于")
.build(app)?;
let help_menu = SubmenuBuilder::new(app, "帮助")
.item(&about)
.build()?;
// ---- Build the menu bar ----
let menu = MenuBuilder::new(app)
.item(&file_menu)
.item(&edit_menu)
.item(&view_menu)
.item(&help_menu)
.build()?;
app.set_menu(menu)?;
// ---- Handle menu events ----
let app_handle = app.handle().clone();
app.on_menu_event(move |_app_handle, event| {
let id = event.id().0.as_str();
match id {
"quit" => {
std::process::exit(0);
}
"open" => {
let handle = app_handle.clone();
// Spawn because dialog is blocking
std::thread::spawn(move || {
if let Some(folder) = handle
.dialog()
.file()
.blocking_pick_folder()
{
let path = folder.to_string();
let _ = handle.emit("folder-opened", path);
}
});
}
"save" => {
let _ = app_handle.emit("menu-save", ());
}
"save_as" => {
let _ = app_handle.emit("menu-save-as", ());
}
"new" => {
let _ = app_handle.emit("menu-new", ());
}
"toggle_outline" => {
let _ = app_handle.emit("toggle-outline", ());
}
"about" => {
let _ = app_handle.emit("show-about", ());
}
_ => {
// Forward other menu events to frontend
let _ = app_handle.emit("menu-event", id);
}
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}