diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f288827..130ec30 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1240,6 +1240,30 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libappindicator" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + [[package]] name = "libc" version = "0.2.138" @@ -1255,6 +1279,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "line-wrap" version = "0.1.1" @@ -2561,6 +2595,7 @@ dependencies = [ "core-foundation", "core-graphics", "crossbeam-channel", + "dirs-next", "dispatch", "gdk", "gdk-pixbuf", @@ -2574,6 +2609,7 @@ dependencies = [ "instant", "jni", "lazy_static", + "libappindicator", "libc", "log", "ndk", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c729c9a..f203471 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -16,8 +16,8 @@ tauri-build = { version = "1.2.1", features = [] } [dependencies] serde_json = "1.0.91" -serde = { version = "1.0.152", features = ["derive"] } -tauri = { version = "1.2.3", features = ["api-all", "devtools", "updater"] } +serde = { version = "1.0.147", features = ["derive"] } +tauri = { version = "1.2.3", features = ["api-all", "devtools", "system-tray", "updater"] } [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index eee64e1..4a2f602 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,18 +1,32 @@ #![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" )] - #[cfg(target_os = "macos")] mod menu; +mod tray; fn main() { - let builder = tauri::Builder::default(); + let builder = tauri::Builder::default(); - #[cfg(target_os = "macos")] - let builder = builder.menu(menu::menu()); + #[cfg(target_os = "macos")] + let builder = builder.menu(menu::menu()); - builder - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} \ No newline at end of file + let builder = builder + .system_tray(tray::system_tray()) + .on_system_tray_event(tray::system_tray_handler); + + builder + .build(tauri::generate_context!()) + .expect("error while building tauri application") + .run(run_event_handler) +} + +fn run_event_handler(app: &tauri::AppHandle, event: tauri::RunEvent) { + match event { + tauri::RunEvent::WindowEvent { label, event, .. } => { + tray::window_event_handler(app, &label, &event); + } + _ => {} + } +} diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs new file mode 100644 index 0000000..2d43c8c --- /dev/null +++ b/src-tauri/src/tray.rs @@ -0,0 +1,87 @@ +use tauri::{ + CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, + SystemTrayMenuItem, WindowEvent, SystemTrayHandle, Window, +}; + +const TRAY_LABEL: &'static str = "main-tray"; + +pub fn window_event_handler( + app: &tauri::AppHandle, + label: &str, + event: &WindowEvent, +) { + match event { + // Prevent Cinny from closing, instead hide it and let it be + // reopened through the tray. + WindowEvent::CloseRequested { api, .. } => { + api.prevent_close(); + app.get_window(&label).unwrap().hide().unwrap(); + app.tray_handle_by_id(TRAY_LABEL) + .unwrap() + .get_item("toggle") + .set_title("Show Cinny") + .unwrap(); + } + _ => {} + } +} + +/// Build the system tray object +pub fn system_tray() -> SystemTray { + let toggle = CustomMenuItem::new("toggle".to_owned(), "Hide Cinny"); + let quit = CustomMenuItem::new("quit".to_owned(), "Quit"); + let menu = SystemTrayMenu::new() + .add_item(toggle) + .add_native_item(SystemTrayMenuItem::Separator) + .add_item(quit); + + tauri::SystemTray::new() + .with_menu(menu) + .with_id(TRAY_LABEL.to_owned()) +} + +pub fn toggle_window_state(window: Window, tray_handle: SystemTrayHandle) { + // Hide the window if it's visible, show it if not + // `is_visible` returns true for minimized state for whatever reason + if window.is_visible().unwrap() { + window.hide().unwrap(); + tray_handle + .get_item("toggle") + .set_title("Show Cinny") + .unwrap(); + } else { + window.unminimize().unwrap(); + window.show().unwrap(); + window.set_focus().unwrap(); + tray_handle + .get_item("toggle") + .set_title("Hide Cinny") + .unwrap(); + }; +} + +pub fn system_tray_handler(app: &tauri::AppHandle, event: SystemTrayEvent) { + let tray_handle = match app.tray_handle_by_id(TRAY_LABEL) { + Some(h) => h, + None => return, + }; + let window = app.get_window("main").unwrap(); + + match event { + SystemTrayEvent::LeftClick { .. } => { + toggle_window_state(window, tray_handle); + } + SystemTrayEvent::MenuItemClick { id, .. } => { + match id.as_str() { + "quit" => { + app.exit(0); + } + "toggle" => { + toggle_window_state(window, tray_handle) + } + _ => {} + } + } + _ => {} + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 0dcb46f..301b15d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -72,6 +72,11 @@ ], "security": { "csp": "script-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" + }, + "systemTray": { + "iconPath": "icons/32x32.png", + "iconAsTemplate": true, + "menuOnLeftClick": false } } }