67e781c886
桌面壳外链和新窗口外链失败改为统一日志记录 桌面壳托盘关闭生命周期和隐藏失败改为统一日志记录 移动壳补齐 iOS Privacy Manifest 与配置门禁 宿主壳方案和项目记忆同步分发约束
211 lines
6.8 KiB
Rust
211 lines
6.8 KiB
Rust
use crate::shell::lifecycle::{
|
|
emit_current_desktop_lifecycle_event, emit_desktop_lifecycle_event,
|
|
log_desktop_host_event_result,
|
|
};
|
|
use tauri::menu::{Menu, MenuItem};
|
|
use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
|
|
use tauri::{Manager, WebviewWindow, WindowEvent};
|
|
|
|
const DESKTOP_TRAY_ID: &str = "genarrative-desktop-tray";
|
|
pub(crate) const TRAY_MENU_SHOW: &str = "show-main-window";
|
|
pub(crate) const TRAY_MENU_RELOAD: &str = "reload-main-window";
|
|
pub(crate) const TRAY_MENU_QUIT: &str = "quit-desktop-shell";
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub(crate) enum DesktopTrayAction {
|
|
ShowMainWindow,
|
|
ReloadMainWindow,
|
|
QuitApp,
|
|
Ignore,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
enum DesktopWindowCloseAction {
|
|
HideToTray,
|
|
CloseWindow,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub(crate) enum DesktopSingleInstanceAction {
|
|
ShowMainWindow,
|
|
}
|
|
|
|
pub(crate) fn resolve_desktop_tray_menu_action(menu_id: &str) -> DesktopTrayAction {
|
|
match menu_id {
|
|
TRAY_MENU_SHOW => DesktopTrayAction::ShowMainWindow,
|
|
TRAY_MENU_RELOAD => DesktopTrayAction::ReloadMainWindow,
|
|
TRAY_MENU_QUIT => DesktopTrayAction::QuitApp,
|
|
_ => DesktopTrayAction::Ignore,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn resolve_desktop_tray_icon_action(
|
|
button: MouseButton,
|
|
button_state: MouseButtonState,
|
|
) -> DesktopTrayAction {
|
|
if button == MouseButton::Left && button_state == MouseButtonState::Up {
|
|
DesktopTrayAction::ShowMainWindow
|
|
} else {
|
|
DesktopTrayAction::Ignore
|
|
}
|
|
}
|
|
|
|
fn resolve_desktop_window_close_action(tray_registered: bool) -> DesktopWindowCloseAction {
|
|
if tray_registered {
|
|
DesktopWindowCloseAction::HideToTray
|
|
} else {
|
|
DesktopWindowCloseAction::CloseWindow
|
|
}
|
|
}
|
|
|
|
pub(crate) fn resolve_desktop_single_instance_action() -> DesktopSingleInstanceAction {
|
|
DesktopSingleInstanceAction::ShowMainWindow
|
|
}
|
|
|
|
pub(crate) fn show_main_window(app: &tauri::AppHandle) -> tauri::Result<()> {
|
|
if let Some(window) = app.get_webview_window("main") {
|
|
window.show()?;
|
|
window.unminimize()?;
|
|
window.set_focus()?;
|
|
emit_current_desktop_lifecycle_event(&window)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn reload_main_window(app: &tauri::AppHandle) -> tauri::Result<()> {
|
|
if let Some(window) = app.get_webview_window("main") {
|
|
window.reload()?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_desktop_tray_action(app: &tauri::AppHandle, action: DesktopTrayAction) {
|
|
match action {
|
|
DesktopTrayAction::ShowMainWindow => {
|
|
log_desktop_host_event_result("tray.show", show_main_window(app));
|
|
}
|
|
DesktopTrayAction::ReloadMainWindow => {
|
|
log_desktop_host_event_result("tray.reload", reload_main_window(app));
|
|
}
|
|
DesktopTrayAction::QuitApp => app.exit(0),
|
|
DesktopTrayAction::Ignore => {}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn register_desktop_tray(app: &tauri::App) -> tauri::Result<()> {
|
|
let show_item = MenuItem::with_id(app, TRAY_MENU_SHOW, "显示主窗口", true, None::<&str>)?;
|
|
let reload_item = MenuItem::with_id(app, TRAY_MENU_RELOAD, "刷新", true, None::<&str>)?;
|
|
let quit_item = MenuItem::with_id(app, TRAY_MENU_QUIT, "退出", true, None::<&str>)?;
|
|
let tray_menu = Menu::with_items(app, &[&show_item, &reload_item, &quit_item])?;
|
|
let mut tray_builder = TrayIconBuilder::with_id(DESKTOP_TRAY_ID)
|
|
.menu(&tray_menu)
|
|
.tooltip("Genarrative")
|
|
.show_menu_on_left_click(false)
|
|
.on_menu_event(|app, event| {
|
|
let menu_id = event.id().0.as_str();
|
|
handle_desktop_tray_action(app, resolve_desktop_tray_menu_action(menu_id));
|
|
})
|
|
.on_tray_icon_event(|tray, event| {
|
|
if let TrayIconEvent::Click {
|
|
button,
|
|
button_state,
|
|
..
|
|
} = event
|
|
{
|
|
handle_desktop_tray_action(
|
|
tray.app_handle(),
|
|
resolve_desktop_tray_icon_action(button, button_state),
|
|
);
|
|
}
|
|
});
|
|
|
|
if let Some(icon) = app.default_window_icon().cloned() {
|
|
tray_builder = tray_builder.icon(icon);
|
|
}
|
|
|
|
tray_builder.build(app)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn register_desktop_window_close_events(window: &WebviewWindow, tray_registered: bool) {
|
|
let close_window = window.clone();
|
|
window.on_window_event(move |event| {
|
|
if let WindowEvent::CloseRequested { api, .. } = event {
|
|
if resolve_desktop_window_close_action(tray_registered)
|
|
== DesktopWindowCloseAction::HideToTray
|
|
{
|
|
api.prevent_close();
|
|
// 中文注释:隐藏 WebView 前先通知 H5 暂停游戏循环和音频,避免隐藏后脚本执行被平台挂起。
|
|
log_desktop_host_event_result(
|
|
"tray.close.lifecycle",
|
|
emit_desktop_lifecycle_event(&close_window, "background", false, "hidden"),
|
|
);
|
|
log_desktop_host_event_result("tray.close.hide", close_window.hide());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn desktop_tray_menu_ids_map_to_real_window_actions() {
|
|
assert_eq!(
|
|
resolve_desktop_tray_menu_action(TRAY_MENU_SHOW),
|
|
DesktopTrayAction::ShowMainWindow
|
|
);
|
|
assert_eq!(
|
|
resolve_desktop_tray_menu_action(TRAY_MENU_RELOAD),
|
|
DesktopTrayAction::ReloadMainWindow
|
|
);
|
|
assert_eq!(
|
|
resolve_desktop_tray_menu_action(TRAY_MENU_QUIT),
|
|
DesktopTrayAction::QuitApp
|
|
);
|
|
assert_eq!(
|
|
resolve_desktop_tray_menu_action("unknown"),
|
|
DesktopTrayAction::Ignore
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn desktop_tray_left_click_restores_main_window_only_on_release() {
|
|
assert_eq!(
|
|
resolve_desktop_tray_icon_action(MouseButton::Left, MouseButtonState::Up),
|
|
DesktopTrayAction::ShowMainWindow
|
|
);
|
|
assert_eq!(
|
|
resolve_desktop_tray_icon_action(MouseButton::Left, MouseButtonState::Down),
|
|
DesktopTrayAction::Ignore
|
|
);
|
|
assert_eq!(
|
|
resolve_desktop_tray_icon_action(MouseButton::Right, MouseButtonState::Up),
|
|
DesktopTrayAction::Ignore
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn desktop_close_hides_to_tray_only_when_tray_is_registered() {
|
|
assert_eq!(
|
|
resolve_desktop_window_close_action(true),
|
|
DesktopWindowCloseAction::HideToTray
|
|
);
|
|
assert_eq!(
|
|
resolve_desktop_window_close_action(false),
|
|
DesktopWindowCloseAction::CloseWindow
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn desktop_single_instance_only_restores_existing_window() {
|
|
assert_eq!(
|
|
resolve_desktop_single_instance_action(),
|
|
DesktopSingleInstanceAction::ShowMainWindow
|
|
);
|
|
}
|
|
}
|