| 1 | //! The app's menu: File, Edit, View, Go, Window and Help, as a desktop app |
| 2 | //! has. On macOS it is the menu bar at the top of the screen, with g1t's |
| 3 | //! own menu first; on Windows and Linux it sits under the window's title, |
| 4 | //! and settings can hide it. The tray's menu (tray.rs) shares its ids, and |
| 5 | //! every menu event, from either, comes to `handle`. |
| 6 | |
| 7 | use tauri::menu::{AboutMetadata, Menu, MenuItem, PredefinedMenuItem, Submenu}; |
| 8 | use tauri::{AppHandle, Manager, Wry}; |
| 9 | use tauri_plugin_opener::OpenerExt; |
| 10 | |
| 11 | use crate::{WINDOW, links, settings, tray, updates}; |
| 12 | |
| 13 | const DOCS: &str = "https://docs.g1t.sh/guides/desktop/"; |
| 14 | const STATUS: &str = "https://status.g1t.sh/"; |
| 15 | |
| 16 | pub fn build(app: &AppHandle) -> tauri::Result<Menu<Wry>> { |
| 17 | let separator = || PredefinedMenuItem::separator(app); |
| 18 | let item = |id: &str, text: &str, accelerator: Option<&str>| MenuItem::with_id(app, id, text, true, accelerator); |
| 19 | |
| 20 | let settings_item = item("settings", if cfg!(target_os = "macos") { "Settings…" } else { "Settings" }, Some("CmdOrCtrl+,"))?; |
| 21 | let check = item("check", "Check for updates…", None)?; |
| 22 | let server = item("server", "Use another g1t…", None)?; |
| 23 | let quit = item("quit", "Quit g1t", Some("CmdOrCtrl+Q"))?; |
| 24 | |
| 25 | let edit = Submenu::with_items( |
| 26 | app, |
| 27 | "Edit", |
| 28 | true, |
| 29 | &[ |
| 30 | &PredefinedMenuItem::undo(app, None)?, |
| 31 | &PredefinedMenuItem::redo(app, None)?, |
| 32 | &separator()?, |
| 33 | &PredefinedMenuItem::cut(app, None)?, |
| 34 | &PredefinedMenuItem::copy(app, None)?, |
| 35 | &PredefinedMenuItem::paste(app, None)?, |
| 36 | &PredefinedMenuItem::select_all(app, None)?, |
| 37 | ], |
| 38 | )?; |
| 39 | let view = Submenu::with_items( |
| 40 | app, |
| 41 | "View", |
| 42 | true, |
| 43 | &[ |
| 44 | &item("reload", "Reload", Some("CmdOrCtrl+R"))?, |
| 45 | &separator()?, |
| 46 | &item("zoom-reset", "Actual size", Some("CmdOrCtrl+0"))?, |
| 47 | &item("zoom-in", "Zoom in", Some("CmdOrCtrl+="))?, |
| 48 | &item("zoom-out", "Zoom out", Some("CmdOrCtrl+-"))?, |
| 49 | &separator()?, |
| 50 | &PredefinedMenuItem::fullscreen(app, None)?, |
| 51 | ], |
| 52 | )?; |
| 53 | let go = Submenu::with_items( |
| 54 | app, |
| 55 | "Go", |
| 56 | true, |
| 57 | &[ |
| 58 | &item("back", "Back", Some(if cfg!(target_os = "macos") { "Cmd+[" } else { "Alt+Left" }))?, |
| 59 | &item("forward", "Forward", Some(if cfg!(target_os = "macos") { "Cmd+]" } else { "Alt+Right" }))?, |
| 60 | &separator()?, |
| 61 | &item("home", "Home", None)?, |
| 62 | &item("notifications", "Notifications", None)?, |
| 63 | ], |
| 64 | )?; |
| 65 | let window = Submenu::with_items( |
| 66 | app, |
| 67 | "Window", |
| 68 | true, |
| 69 | &[&PredefinedMenuItem::minimize(app, None)?, &PredefinedMenuItem::maximize(app, None)?], |
| 70 | )?; |
| 71 | let help = Submenu::with_items(app, "Help", true, &[&item("docs", "Desktop app guide", None)?, &item("status", "g1t.sh status", None)?, &separator()?, &check])?; |
| 72 | // macOS keeps About in g1t's own menu; elsewhere it closes Help. |
| 73 | #[cfg(not(target_os = "macos"))] |
| 74 | { |
| 75 | help.append(&separator()?)?; |
| 76 | help.append(&PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?)?; |
| 77 | } |
| 78 | |
| 79 | #[cfg(target_os = "macos")] |
| 80 | { |
| 81 | let g1t = Submenu::with_items( |
| 82 | app, |
| 83 | "g1t", |
| 84 | true, |
| 85 | &[ |
| 86 | &PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?, |
| 87 | &separator()?, |
| 88 | &settings_item, |
| 89 | &server, |
| 90 | &separator()?, |
| 91 | &PredefinedMenuItem::services(app, None)?, |
| 92 | &separator()?, |
| 93 | &PredefinedMenuItem::hide(app, None)?, |
| 94 | &PredefinedMenuItem::hide_others(app, None)?, |
| 95 | &PredefinedMenuItem::show_all(app, None)?, |
| 96 | &separator()?, |
| 97 | &quit, |
| 98 | ], |
| 99 | )?; |
| 100 | let file = Submenu::with_items(app, "File", true, &[&PredefinedMenuItem::close_window(app, None)?])?; |
| 101 | Menu::with_items(app, &[&g1t, &file, &edit, &view, &go, &window, &help]) |
| 102 | } |
| 103 | #[cfg(not(target_os = "macos"))] |
| 104 | { |
| 105 | let file = Submenu::with_items( |
| 106 | app, |
| 107 | "File", |
| 108 | true, |
| 109 | &[&settings_item, &server, &separator()?, &item("close", "Close window", Some("CmdOrCtrl+W"))?, &quit], |
| 110 | )?; |
| 111 | Menu::with_items(app, &[&file, &edit, &view, &go, &window, &help]) |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | fn about() -> AboutMetadata<'static> { |
| 116 | AboutMetadata { |
| 117 | name: Some("g1t".into()), |
| 118 | version: Some(env!("CARGO_PKG_VERSION").into()), |
| 119 | copyright: Some("Flagon, Inc.".into()), |
| 120 | website: Some("https://g1t.sh".into()), |
| 121 | website_label: Some("g1t.sh".into()), |
| 122 | ..Default::default() |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | /// Runs the script on the main window's page. |
| 127 | fn eval(app: &AppHandle, script: &str) { |
| 128 | if let Some(window) = app.get_webview_window(WINDOW) { |
| 129 | let _ = window.eval(script); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | /// A menu row was chosen, from the app's menu or the tray's. |
| 134 | pub fn handle(app: &AppHandle, id: &str) { |
| 135 | match id { |
| 136 | "open" => tray::show(app), |
| 137 | "settings" => settings::open(app), |
| 138 | "server" => tray::launcher(app, "change"), |
| 139 | "check" => { |
| 140 | let app = app.clone(); |
| 141 | tauri::async_runtime::spawn(async move { |
| 142 | let _ = updates::check(&app, true).await; |
| 143 | }); |
| 144 | } |
| 145 | "restart" => updates::restart(app), |
| 146 | "quit" => updates::quit(app), |
| 147 | "close" => tray::close(app), |
| 148 | "reload" => { |
| 149 | if let Some(window) = app.get_webview_window(WINDOW) { |
| 150 | let _ = window.reload(); |
| 151 | } |
| 152 | } |
| 153 | "zoom-in" => settings::zoom(app, 1), |
| 154 | "zoom-out" => settings::zoom(app, -1), |
| 155 | "zoom-reset" => settings::zoom(app, 0), |
| 156 | "back" => eval(app, "history.back()"), |
| 157 | "forward" => eval(app, "history.forward()"), |
| 158 | "home" => links::go(app, "/"), |
| 159 | "notifications" => links::go(app, "/notifications"), |
| 160 | "docs" => { |
| 161 | let _ = app.opener().open_url(DOCS, None::<&str>); |
| 162 | } |
| 163 | "status" => { |
| 164 | let _ = app.opener().open_url(STATUS, None::<&str>); |
| 165 | } |
| 166 | _ => {} |
| 167 | } |
| 168 | } |