Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/optional-custom-titlebar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: patch
---

Allow using native window chrome instead of the custom title bar.
27 changes: 25 additions & 2 deletions src-tauri/src/desktop/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,34 @@ use ts_rs::TS;
pub struct DesktopSettings {
pub close_to_background_on_close: bool,
pub show_system_tray_icon: bool,
pub use_custom_title_bar: bool,
}

pub(crate) const DESKTOP_SETTINGS_PATH: &str = "desktop-preferences.json";
pub(crate) const CLOSE_TO_BACKGROUND_ON_CLOSE_KEY: &str = "closeToBackgroundOnClose";
pub(crate) const SHOW_SYSTEM_TRAY_ICON_KEY: &str = "showSystemTrayIcon";
pub(crate) const USE_CUSTOM_TITLE_BAR_KEY: &str = "useCustomTitleBar";
pub(crate) const LEGACY_KEEP_BACKGROUND_RUNNING_KEY: &str = "keepBackgroundRunning";

pub(crate) const fn use_custom_title_bar_default() -> bool {
cfg!(target_os = "windows")
}

pub(crate) fn tray_available_for_session(settings: DesktopSettings, tray_created: bool) -> bool {
settings.show_system_tray_icon && tray_created
}

pub(crate) fn desktop_settings_from_values(
close_to_background_on_close: Option<bool>,
show_system_tray_icon: Option<bool>,
use_custom_title_bar: Option<bool>,
keep_background_running: Option<bool>,
) -> DesktopSettings {
DesktopSettings {
close_to_background_on_close: close_to_background_on_close.unwrap_or(true)
|| keep_background_running.unwrap_or(false),
show_system_tray_icon: show_system_tray_icon.unwrap_or(true),
use_custom_title_bar: use_custom_title_bar.unwrap_or(use_custom_title_bar_default()),
}
}

Expand All @@ -41,13 +49,15 @@ mod tests {
let settings = DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: false,
use_custom_title_bar: true,
};

assert_eq!(
serde_json::to_value(settings).unwrap(),
json!({
"closeToBackgroundOnClose": true,
"showSystemTrayIcon": false,
"useCustomTitleBar": true,
})
);
}
Expand All @@ -58,11 +68,13 @@ mod tests {
serde_json::from_value::<DesktopSettings>(json!({
"closeToBackgroundOnClose": true,
"showSystemTrayIcon": false,
"useCustomTitleBar": true,
}))
.unwrap(),
DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: false,
use_custom_title_bar: true,
}
);
}
Expand All @@ -74,28 +86,39 @@ mod tests {
assert!(output.contains("type DesktopSettings"));
assert!(output.contains("closeToBackgroundOnClose: boolean"));
assert!(output.contains("showSystemTrayIcon: boolean"));
assert!(output.contains("useCustomTitleBar: boolean"));
assert!(!output.contains("keepBackgroundRunning: boolean"));
}

#[test]
fn legacy_background_setting_keeps_close_behavior_enabled() {
assert_eq!(
desktop_settings_from_values(Some(false), Some(false), Some(true)),
desktop_settings_from_values(Some(false), Some(false), Some(false), Some(true)),
DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: false,
use_custom_title_bar: false,
}
);
}

#[test]
fn explicit_close_setting_stays_disabled_when_legacy_background_is_off() {
assert_eq!(
desktop_settings_from_values(Some(false), Some(true), Some(false)),
desktop_settings_from_values(Some(false), Some(true), Some(true), Some(false)),
DesktopSettings {
close_to_background_on_close: false,
show_system_tray_icon: true,
use_custom_title_bar: true,
}
);
}

#[test]
fn missing_custom_title_bar_setting_uses_platform_default() {
assert_eq!(
desktop_settings_from_values(Some(true), Some(true), None, None).use_custom_title_bar,
cfg!(target_os = "windows")
);
}
}
65 changes: 58 additions & 7 deletions src-tauri/src/desktop/tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use std::sync::atomic::{AtomicBool, Ordering};

use crate::desktop::runtime_state::DesktopRuntimeState;
use crate::desktop::settings::{
desktop_settings_from_values, tray_available_for_session, DesktopSettings,
CLOSE_TO_BACKGROUND_ON_CLOSE_KEY, DESKTOP_SETTINGS_PATH, LEGACY_KEEP_BACKGROUND_RUNNING_KEY,
SHOW_SYSTEM_TRAY_ICON_KEY,
desktop_settings_from_values, tray_available_for_session, use_custom_title_bar_default,
DesktopSettings, CLOSE_TO_BACKGROUND_ON_CLOSE_KEY, DESKTOP_SETTINGS_PATH,
LEGACY_KEEP_BACKGROUND_RUNNING_KEY, SHOW_SYSTEM_TRAY_ICON_KEY, USE_CUSTOM_TITLE_BAR_KEY,
};
use serde_json::json;
use tauri::{
Expand All @@ -24,6 +24,7 @@ const TRAY_MENU_QUIT_ID: &str = "tray_quit";
pub struct DesktopSettingsState {
close_to_background_on_close: AtomicBool,
show_system_tray_icon: AtomicBool,
use_custom_title_bar: AtomicBool,
tray_available: AtomicBool,
}

Expand All @@ -32,6 +33,7 @@ impl Default for DesktopSettingsState {
Self {
close_to_background_on_close: AtomicBool::new(true),
show_system_tray_icon: AtomicBool::new(true),
use_custom_title_bar: AtomicBool::new(use_custom_title_bar_default()),
tray_available: AtomicBool::new(false),
}
}
Expand Down Expand Up @@ -65,12 +67,18 @@ fn exit_request_action(settings: DesktopSettings, code: Option<i32>) -> ExitRequ
}
}

fn load_desktop_settings(app: &AppHandle<crate::BrowserEngine>) -> tauri::Result<DesktopSettings> {
pub(crate) fn load_desktop_settings(
app: &AppHandle<crate::BrowserEngine>,
) -> tauri::Result<DesktopSettings> {
let store = app
.store_builder(DESKTOP_SETTINGS_PATH)
.defaults(std::collections::HashMap::from([
(CLOSE_TO_BACKGROUND_ON_CLOSE_KEY.into(), json!(true)),
(SHOW_SYSTEM_TRAY_ICON_KEY.into(), json!(true)),
(
USE_CUSTOM_TITLE_BAR_KEY.into(),
json!(use_custom_title_bar_default()),
),
]))
.build()
.map_err(|error| tauri::Error::PluginInitialization("store".into(), error.to_string()))?;
Expand All @@ -82,6 +90,9 @@ fn load_desktop_settings(app: &AppHandle<crate::BrowserEngine>) -> tauri::Result
store
.get(SHOW_SYSTEM_TRAY_ICON_KEY)
.and_then(|value| value.as_bool()),
store
.get(USE_CUSTOM_TITLE_BAR_KEY)
.and_then(|value| value.as_bool()),
store
.get(LEGACY_KEEP_BACKGROUND_RUNNING_KEY)
.and_then(|value| value.as_bool()),
Expand All @@ -93,6 +104,7 @@ fn current_desktop_settings(app: &AppHandle<crate::BrowserEngine>) -> DesktopSet
DesktopSettings {
close_to_background_on_close: state.close_to_background_on_close.load(Ordering::Relaxed),
show_system_tray_icon: state.show_system_tray_icon.load(Ordering::Relaxed),
use_custom_title_bar: state.use_custom_title_bar.load(Ordering::Relaxed),
}
}

Expand Down Expand Up @@ -137,6 +149,11 @@ fn apply_desktop_settings(
state
.show_system_tray_icon
.store(settings.show_system_tray_icon, Ordering::Relaxed);
state
.use_custom_title_bar
.store(settings.use_custom_title_bar, Ordering::Relaxed);

apply_main_window_title_bar_settings(app, settings)?;

if settings.show_system_tray_icon && cfg!(not(target_os = "macos")) {
if app.tray_by_id(MAIN_TRAY_ID).is_none() {
Expand Down Expand Up @@ -167,6 +184,33 @@ fn apply_desktop_settings(
Ok(desktop_runtime_state(app))
}

fn apply_main_window_title_bar_settings(
app: &AppHandle<crate::BrowserEngine>,
settings: DesktopSettings,
) -> tauri::Result<()> {
let Some(window) = app.get_webview_window(crate::MAIN_WINDOW_LABEL) else {
return Ok(());
};

#[cfg(any(target_os = "windows", target_os = "linux"))]
window.set_decorations(!settings.use_custom_title_bar)?;

#[cfg(target_os = "macos")]
{
window.set_title_bar_style(if settings.use_custom_title_bar {
tauri::TitleBarStyle::Transparent
} else {
tauri::TitleBarStyle::Visible
})?;
window.set_title(if settings.use_custom_title_bar {
""
} else {
crate::main_window_title(app)
})?;
}

Ok(())
}
fn close_all_windows(app: &AppHandle<crate::BrowserEngine>) {
for (_label, window) in app.webview_windows() {
let _ = window.close();
Expand Down Expand Up @@ -303,6 +347,7 @@ mod tests {
let settings = DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: false,
use_custom_title_bar: false,
};

assert_eq!(
Expand All @@ -316,6 +361,7 @@ mod tests {
let settings = DesktopSettings {
show_system_tray_icon: true,
close_to_background_on_close: false,
use_custom_title_bar: false,
};

assert_eq!(
Expand All @@ -329,6 +375,7 @@ mod tests {
let settings = DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: true,
use_custom_title_bar: false,
};

assert_eq!(
Expand All @@ -343,6 +390,7 @@ mod tests {
let settings = DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: true,
use_custom_title_bar: false,
};

assert_eq!(
Expand All @@ -354,32 +402,35 @@ mod tests {
#[test]
fn missing_store_values_default_to_enabled() {
assert_eq!(
desktop_settings_from_values(None, None, None),
desktop_settings_from_values(None, None, None, None),
DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: true,
use_custom_title_bar: use_custom_title_bar_default(),
}
);
}

#[test]
fn legacy_background_store_value_migrates_to_close_behavior() {
assert_eq!(
desktop_settings_from_values(Some(false), Some(false), Some(true)),
desktop_settings_from_values(Some(false), Some(false), Some(false), Some(true)),
DesktopSettings {
close_to_background_on_close: true,
show_system_tray_icon: false,
use_custom_title_bar: false,
}
);
}

#[test]
fn explicit_store_values_are_preserved_when_legacy_background_is_off() {
assert_eq!(
desktop_settings_from_values(Some(false), Some(false), Some(false)),
desktop_settings_from_values(Some(false), Some(false), Some(true), Some(false)),
DesktopSettings {
show_system_tray_icon: false,
close_to_background_on_close: false,
use_custom_title_bar: true,
}
);
}
Expand Down
Loading
Loading