diff --git a/desktop/i18n/en_US.properties b/desktop/i18n/en_US.properties
index f1ee198..9abe602 100644
--- a/desktop/i18n/en_US.properties
+++ b/desktop/i18n/en_US.properties
@@ -115,6 +115,8 @@ menu-theme-light=Light
menu-theme-dark=Dark
menu-help=Help
menu-about=About Orbien Desktop
+tray-quit=Quit
+tray-tooltip=Orbien
about-window-title=About Orbien Desktop
about-page-title=About
about-tagline=Orbien intranet-penetration cross-platform native desktop client — visual config, ready out of the box
diff --git a/desktop/i18n/zh_CN.properties b/desktop/i18n/zh_CN.properties
index d4d1f18..cd903bf 100644
--- a/desktop/i18n/zh_CN.properties
+++ b/desktop/i18n/zh_CN.properties
@@ -110,6 +110,8 @@ menu-theme-light=白昼
menu-theme-dark=黑夜
menu-help=帮助
menu-about=关于 Orbien Desktop
+tray-quit=退出
+tray-tooltip=Orbien
about-window-title=关于 Orbien Desktop
about-page-title=关于
about-tagline=Orbien内网穿透跨平台原生桌面客户端,可视化配置,开箱即用
diff --git a/desktop/src/main.rs b/desktop/src/main.rs
index f61bf27..5a8d9ac 100644
--- a/desktop/src/main.rs
+++ b/desktop/src/main.rs
@@ -25,6 +25,13 @@ fn locale_of(ui: &AppWindow) -> Locale {
Locale::from_index(ui.get_locale_index())
}
+fn sync_tray_locale(tray: &OrbienTray, locale_index: i32) {
+ let tr = tray.global::
();
+ if tr.get_locale_index() != locale_index {
+ tr.set_locale_index(locale_index);
+ }
+}
+
fn apply_theme(ui: &AppWindow, theme_index: i32) {
ui.set_theme_index(theme_index);
}
@@ -671,6 +678,16 @@ fn main() -> Result<(), slint::PlatformError> {
ui.set_locale_index(prefs.locale_index());
let _ = ui.global::
().set_locale_index(ui.get_locale_index());
apply_theme(&ui, prefs.theme_index());
+
+ let tray = OrbienTray::new()?;
+ sync_tray_locale(&tray, ui.get_locale_index());
+ let tray_weak = tray.as_weak();
+ tray.on_quit(|| {
+ runtime::stop_async(|| {
+ let _ = slint::quit_event_loop();
+ });
+ });
+
let default_path = config_bridge::default_config_path();
ui.set_config_file_path(config_bridge::path_display(&default_path).into());
@@ -912,7 +929,7 @@ fn main() -> Result<(), slint::PlatformError> {
}
});
- wire_tunnel_and_config(&ui, remotes_gen.clone());
+ wire_tunnel_and_config(&ui, remotes_gen.clone(), tray_weak);
let ui_weak = ui.as_weak();
ui.on_show_about(move || {
@@ -946,7 +963,9 @@ fn main() -> Result<(), slint::PlatformError> {
ui.show()?;
center_window_on_screen(&ui);
- ui.run()
+ let result = ui.run();
+ drop(tray);
+ result
}
fn center_window_on_screen(ui: &AppWindow) {
@@ -994,7 +1013,11 @@ fn open_url(url: &str) -> std::io::Result<()> {
Ok(())
}
-fn wire_tunnel_and_config(ui: &AppWindow, remotes_gen: Arc>) {
+fn wire_tunnel_and_config(
+ ui: &AppWindow,
+ remotes_gen: Arc>,
+ tray_weak: slint::Weak,
+) {
let ui_weak = ui.as_weak();
ui.on_tunnel_add(move || {
if let Some(ui) = ui_weak.upgrade() {
@@ -1298,12 +1321,16 @@ fn wire_tunnel_and_config(ui: &AppWindow, remotes_gen: Arc>) {
let ui_weak = ui.as_weak();
ui.on_locale_changed(move |index| {
- if let Some(ui) = ui_weak.upgrade() {
- ui.global::().set_locale_index(index);
- let label = if index == 0 { "zh-CN" } else { "en-US" };
- push_log(&ui, &format!("INFO locale set to {label}"));
- persist_prefs(&ui);
+ let Some(ui) = ui_weak.upgrade() else {
+ return;
+ };
+ ui.global::
().set_locale_index(index);
+ if let Some(tray) = tray_weak.upgrade() {
+ sync_tray_locale(&tray, index);
}
+ let label = if index == 0 { "zh-CN" } else { "en-US" };
+ push_log(&ui, &format!("INFO locale set to {label}"));
+ persist_prefs(&ui);
});
let ui_weak = ui.as_weak();
diff --git a/desktop/ui/app.slint b/desktop/ui/app.slint
index dab9690..b08ba10 100644
--- a/desktop/ui/app.slint
+++ b/desktop/ui/app.slint
@@ -12,8 +12,9 @@ import { AboutDialog } from "pages/about.slint";
import { ToastHost } from "toast.slint";
import { Tr } from "i18n.slint";
import { AppMeta } from "version.slint";
+import { OrbienTray } from "tray.slint";
-export { AppPage, TunnelRow, LogLine, Tr, AboutDialog, AppMeta, Theme }
+export { AppPage, TunnelRow, LogLine, Tr, AboutDialog, AppMeta, Theme, OrbienTray }
export component AppWindow inherits Window {
title: "Orbien Desktop";
default-font-family: "Noto Sans SC";
diff --git a/desktop/ui/tray.slint b/desktop/ui/tray.slint
new file mode 100644
index 0000000..c12065e
--- /dev/null
+++ b/desktop/ui/tray.slint
@@ -0,0 +1,17 @@
+import { Tr } from "i18n.slint";
+
+export component OrbienTray inherits SystemTrayIcon {
+ icon: @image-url("../assets/logo.png");
+ tooltip: Tr.tray-tooltip;
+
+ callback quit();
+
+ Menu {
+ MenuItem {
+ title: Tr.tray-quit;
+ activated => {
+ root.quit();
+ }
+ }
+ }
+}