Skip to content
93 linesCodeBlameRaw
1//! The window's frame. g1t draws its own title bar, as Slack and Discord
2//! do, so the window is all g1t and looks the same on every system: on
3//! Windows and Linux the system's frame goes and the bar has the menu
4//! button, back and forward, search, and minimize, maximize and close; on
5//! macOS the bar sits under the window's own traffic lights. The bar
6//! itself is bridge.js's, drawn on every page the window shows, the
7//! site's, the launcher's, and GitHub's while signing in; the site makes
8//! room for it (`--frame-top`, apps/web/app/app.css). Settings can give
9//! the window back its system frame, and the menu bar with it, from the
10//! next start: the bar is a script each window gets as it opens.
11
12use tauri::{AppHandle, LogicalPosition, Manager, Runtime, WebviewWindow, WebviewWindowBuilder};
13
14use crate::{WINDOW, config, tray};
15
16/// The bar's height, in CSS pixels; bridge.js draws it this tall.
17pub const HEIGHT: f64 = 36.0;
18
19/// Whether g1t draws its own title bar, as the settings say now.
20pub fn custom(app: &AppHandle) -> bool {
21 !config::settings(app).system_title_bar
22}
23
24/// The title bar g1t started with, which its windows keep until it restarts.
25static STARTED_CUSTOM: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
26
27/// Whether this run draws g1t's own title bar: decided once, at the start.
28pub fn started(app: &AppHandle) -> bool {
29 *STARTED_CUSTOM.get_or_init(|| custom(app))
30}
31
32/// A window with g1t's title bar, or the system's.
33pub fn style<'a, R: Runtime, M: Manager<R>>(builder: WebviewWindowBuilder<'a, R, M>, custom: bool) -> WebviewWindowBuilder<'a, R, M> {
34 if !custom {
35 return builder;
36 }
37 #[cfg(target_os = "macos")]
38 {
39 // The traffic lights stay, over the bar and centred in it.
40 builder
41 .title_bar_style(tauri::TitleBarStyle::Overlay)
42 .hidden_title(true)
43 .traffic_light_position(LogicalPosition::new(14.0, (HEIGHT - 14.0) / 2.0))
44 }
45 #[cfg(not(target_os = "macos"))]
46 {
47 builder.decorations(false)
48 }
49}
50
51/// The main window's menu bar: shown under the system's frame, and kept
52/// hidden under g1t's, whose menu button opens the same menu (`menu`) and
53/// whose shortcuts still work.
54pub fn menu_bar(window: &WebviewWindow, custom: bool) {
55 let _ = if custom { window.hide_menu() } else { window.show_menu() };
56 // GTK shows everything in a window each time the window is shown, the
57 // hidden menu bar too, unless the menu bar is told to sit that out.
58 #[cfg(target_os = "linux")]
59 if let Ok(vbox) = window.default_vbox() {
60 use gtk::prelude::*;
61 for child in vbox.children().into_iter().filter(|child| child.is::<gtk::MenuBar>()) {
62 child.set_no_show_all(custom);
63 }
64 }
65}
66
67/// What a button on the bar asks of the window it is in.
68pub fn control(app: &AppHandle, window: &WebviewWindow, action: &str) -> Result<bool, String> {
69 let main = window.label() == WINDOW;
70 match action {
71 "minimize" => window.minimize().map_err(|error| error.to_string())?,
72 "maximize" => {
73 let maximized = window.is_maximized().unwrap_or(false);
74 let _ = if maximized { window.unmaximize() } else { window.maximize() };
75 }
76 "close" if main => tray::close(app),
77 "close" => window.close().map_err(|error| error.to_string())?,
78 "drag" => window.start_dragging().map_err(|error| error.to_string())?,
79 "maximized" => {}
80 other => return Err(format!("the window cannot {other}")),
81 }
82 Ok(window.is_maximized().unwrap_or(false))
83}
84
85/// The menu button: the window's menu, under the button.
86pub fn menu(window: &WebviewWindow, x: f64, y: f64) -> Result<(), String> {
87 #[cfg(target_os = "macos")]
88 let menu = window.app_handle().menu();
89 #[cfg(not(target_os = "macos"))]
90 let menu = window.menu();
91 let menu = menu.ok_or("the window has no menu")?;
92 window.popup_menu_at(&menu, LogicalPosition::new(x, y)).map_err(|error| error.to_string())
93}