Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 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 | } |