Skip to content
170 linesCodeBlameRaw
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
7use tauri::menu::{AboutMetadata, Menu, MenuItem, PredefinedMenuItem, Submenu};
8use tauri::{AppHandle, Wry};
9use tauri_plugin_opener::OpenerExt;
10
11use crate::{links, settings, tray, updates};
12
13const DOCS: &str = "https://docs.g1t.sh/guides/desktop/";
14const STATUS: &str = "https://status.g1t.sh/";
15
16pub 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(
72 app,
73 "Help",
74 true,
75 &[&item("docs", "Desktop app guide", None)?, &item("status", "g1t.sh status", None)?, &separator()?, &check, &tray::restart_item(app)?],
76 )?;
77 // macOS keeps About in g1t's own menu; elsewhere it closes Help.
78 #[cfg(not(target_os = "macos"))]
79 {
80 help.append(&separator()?)?;
81 help.append(&PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?)?;
82 }
83
84 #[cfg(target_os = "macos")]
85 {
86 let g1t = Submenu::with_items(
87 app,
88 "g1t",
89 true,
90 &[
91 &PredefinedMenuItem::about(app, Some("About g1t"), Some(about()))?,
92 &separator()?,
93 &settings_item,
94 &server,
95 &separator()?,
96 &PredefinedMenuItem::services(app, None)?,
97 &separator()?,
98 &PredefinedMenuItem::hide(app, None)?,
99 &PredefinedMenuItem::hide_others(app, None)?,
100 &PredefinedMenuItem::show_all(app, None)?,
101 &separator()?,
102 &quit,
103 ],
104 )?;
105 let file = Submenu::with_items(app, "File", true, &[&PredefinedMenuItem::close_window(app, None)?])?;
106 Menu::with_items(app, &[&g1t, &file, &edit, &view, &go, &window, &help])
107 }
108 #[cfg(not(target_os = "macos"))]
109 {
110 let file = Submenu::with_items(
111 app,
112 "File",
113 true,
114 &[&settings_item, &server, &separator()?, &item("close", "Close window", Some("CmdOrCtrl+W"))?, &quit],
115 )?;
116 Menu::with_items(app, &[&file, &edit, &view, &go, &window, &help])
117 }
118}
119
120fn about() -> AboutMetadata<'static> {
121 AboutMetadata {
122 name: Some("g1t".into()),
123 version: Some(env!("CARGO_PKG_VERSION").into()),
124 copyright: Some("Flagon, Inc.".into()),
125 website: Some("https://g1t.sh".into()),
126 website_label: Some("g1t.sh".into()),
127 ..Default::default()
128 }
129}
130
131/// Runs the script on the main window's page.
132fn eval(app: &AppHandle, script: &str) {
133 if let Some(site) = crate::frame::site(app) {
134 let _ = site.eval(script);
135 }
136}
137
138/// A menu row was chosen, from the app's menu or the tray's.
139pub fn handle(app: &AppHandle, id: &str) {
140 match id {
141 "open" => tray::show(app),
142 "settings" | "runner-status" => settings::open(app),
143 "runner-toggle" => crate::runner::toggle(app),
144 "server" => tray::launcher(app, "change"),
145 // The settings window asks, and shows the answer where it cannot be missed.
146 "check" => settings::open_and_check(app),
147 "restart" => updates::restart(app),
148 "quit" => updates::quit(app),
149 "close" => tray::close(app),
150 "reload" => {
151 if let Some(site) = crate::frame::site(app) {
152 let _ = site.reload();
153 }
154 }
155 "zoom-in" => settings::zoom(app, 1),
156 "zoom-out" => settings::zoom(app, -1),
157 "zoom-reset" => settings::zoom(app, 0),
158 "back" => eval(app, "history.back()"),
159 "forward" => eval(app, "history.forward()"),
160 "home" => links::go(app, "/"),
161 "notifications" => links::go(app, "/notifications"),
162 "docs" => {
163 let _ = app.opener().open_url(DOCS, None::<&str>);
164 }
165 "status" => {
166 let _ = app.opener().open_url(STATUS, None::<&str>);
167 }
168 _ => {}
169 }
170}