Sfoglia il codice sorgente

feat(desktop-pet): 托盘菜单、单实例与主进程组装

liaoxg 2 giorni fa
parent
commit
75a216f97c

+ 28 - 0
cfc-desktop-pet/src-tauri/src/commands.rs

@@ -0,0 +1,28 @@
+#[tauri::command]
+pub async fn on_interact(_app: tauri::AppHandle, _action: String, _data: Option<String>) -> Result<(), String> {
+    Ok(())
+}
+
+#[tauri::command]
+pub async fn drag_start(_app: tauri::AppHandle) -> Result<(), String> {
+    Ok(())
+}
+
+#[tauri::command]
+pub async fn drag_end() -> Result<(), String> {
+    Ok(())
+}
+
+#[tauri::command]
+pub fn get_settings(_app: tauri::AppHandle) -> Result<serde_json::Value, String> {
+    Ok(serde_json::json!({}))
+}
+
+#[tauri::command]
+pub fn save_settings(
+    _app: tauri::AppHandle,
+    _base_url: String,
+    _token: Option<String>,
+) -> Result<(), String> {
+    Ok(())
+}

+ 38 - 1
cfc-desktop-pet/src-tauri/src/main.rs

@@ -2,14 +2,51 @@
 
 mod api;
 mod app_state;
+mod commands;
 mod config;
 mod poller;
 mod settings;
 mod state;
 mod token;
+mod tray;
+
+use app_state::AppState;
+use std::sync::Arc;
+use tauri::Manager;
 
 fn main() {
     tauri::Builder::default()
+        // 单实例插件必须最先注册
+        .plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
+            if let Some(win) = app.get_webview_window("main") {
+                let _ = win.show();
+                let _ = win.set_focus();
+            }
+        }))
+        .setup(|app| {
+            let config_dir = app.path().app_config_dir().map_err(|e| e.to_string())?;
+            let settings = settings::SettingsStore::new(config_dir);
+            let cfg = settings.load();
+            let token = token::TokenStore::new().load().ok().flatten().unwrap_or_else(|| "dev-token".into());
+            let api = Arc::new(api::ApiClient::new(cfg.base_url.clone(), token));
+
+            let handle = poller::spawn(app.handle().clone(), api.clone());
+            app.manage(settings);
+            app.manage(AppState {
+                passthrough: std::sync::Mutex::new(false),
+                poll_handle: std::sync::Mutex::new(Some(handle)),
+                api,
+            });
+            tray::setup_tray(app)?;
+            Ok(())
+        })
+        .invoke_handler(tauri::generate_handler![
+            commands::on_interact,
+            commands::drag_start,
+            commands::drag_end,
+            commands::get_settings,
+            commands::save_settings,
+        ])
         .run(tauri::generate_context!())
         .expect("error while running tauri application");
-}
+}

+ 65 - 0
cfc-desktop-pet/src-tauri/src/tray.rs

@@ -0,0 +1,65 @@
+use crate::app_state::AppState;
+use tauri::menu::{MenuBuilder, MenuItemBuilder};
+use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
+use tauri::{App, Manager};
+
+pub fn setup_tray(app: &App) -> tauri::Result<()> {
+    let settings_i = MenuItemBuilder::with_id("settings", "设置").build(app)?;
+    let toggle_i = MenuItemBuilder::with_id("toggle", "显示/隐藏").build(app)?;
+    let passthrough_i = MenuItemBuilder::with_id("passthrough", "鼠标穿透").build(app)?;
+    let quit_i = MenuItemBuilder::with_id("quit", "退出").build(app)?;
+    let menu = MenuBuilder::new(app)
+        .items(&[&settings_i, &toggle_i, &passthrough_i, &quit_i])
+        .build()?;
+
+    let _tray = TrayIconBuilder::new()
+        .icon(app.default_window_icon().expect("bundle icon").clone())
+        .menu(&menu)
+        .show_menu_on_left_click(false)
+        .on_menu_event(|app, event| match event.id().as_ref() {
+            "settings" => show_settings(app),
+            "toggle" => toggle_main(app),
+            "passthrough" => toggle_passthrough(app),
+            "quit" => app.exit(0),
+            _ => {}
+        })
+        .on_tray_icon_event(|tray, event| {
+            if let TrayIconEvent::Click {
+                button: MouseButton::Left,
+                button_state: MouseButtonState::Up,
+                ..
+            } = event
+            {
+                toggle_main(tray.app_handle());
+            }
+        })
+        .build(app)?;
+
+    Ok(())
+}
+
+pub fn show_settings(app: &tauri::AppHandle) {
+    if let Some(win) = app.get_webview_window("settings") {
+        let _ = win.show();
+        let _ = win.set_focus();
+    }
+}
+
+pub fn toggle_main(app: &tauri::AppHandle) {
+    if let Some(win) = app.get_webview_window("main") {
+        match win.is_visible() {
+            Ok(true) => { let _ = win.hide(); }
+            _ => { let _ = win.show(); }
+        }
+    }
+}
+
+pub fn toggle_passthrough(app: &tauri::AppHandle) {
+    if let Some(state) = app.try_state::<AppState>() {
+        let mut passthrough = state.passthrough.lock().unwrap();
+        *passthrough = !*passthrough;
+        if let Some(win) = app.get_webview_window("main") {
+            let _ = win.set_ignore_cursor_events(*passthrough);
+        }
+    }
+}