Skip to content

Commit 321f3fe

Browse files
feat(macos): title_bar_style and hidden_title window options, closes #2663 (#3965)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
1 parent 39443b4 commit 321f3fe

File tree

11 files changed

+212
-5
lines changed

11 files changed

+212
-5
lines changed

Diff for: .changes/hidden-title-macos.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri': minor
3+
"tauri-runtime-wry": minor
4+
---
5+
6+
Add `hidden_title` option for macOS windows.

Diff for: .changes/transparent-titlebar-macos.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri': minor
3+
"tauri-runtime-wry": minor
4+
---
5+
6+
Add `title_bar_style` option for macOS windows.

Diff for: core/tauri-runtime-wry/src/lib.rs

+35-4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ use wry::application::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
3535
#[cfg(windows)]
3636
use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
3737

38+
#[cfg(target_os = "macos")]
39+
use tauri_utils::TitleBarStyle;
3840
use tauri_utils::{config::WindowConfig, debug_eprintln, Theme};
3941
use uuid::Uuid;
4042
use wry::{
@@ -739,6 +741,13 @@ impl WindowBuilder for WindowBuilderWrapper {
739741
.skip_taskbar(config.skip_taskbar)
740742
.theme(config.theme);
741743

744+
#[cfg(target_os = "macos")]
745+
{
746+
window = window
747+
.hidden_title(config.hidden_title)
748+
.title_bar_style(config.title_bar_style);
749+
}
750+
742751
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
743752
{
744753
window = window.transparent(config.transparent);
@@ -879,6 +888,32 @@ impl WindowBuilder for WindowBuilderWrapper {
879888
self
880889
}
881890

891+
#[cfg(target_os = "macos")]
892+
fn title_bar_style(mut self, style: TitleBarStyle) -> Self {
893+
match style {
894+
TitleBarStyle::Visible => {
895+
self.inner = self.inner.with_titlebar_transparent(false);
896+
// Fixes rendering issue when resizing window with devtools open (https://github.com/tauri-apps/tauri/issues/3914)
897+
self.inner = self.inner.with_fullsize_content_view(true);
898+
}
899+
TitleBarStyle::Transparent => {
900+
self.inner = self.inner.with_titlebar_transparent(true);
901+
self.inner = self.inner.with_fullsize_content_view(false);
902+
}
903+
TitleBarStyle::Overlay => {
904+
self.inner = self.inner.with_titlebar_transparent(true);
905+
self.inner = self.inner.with_fullsize_content_view(true);
906+
}
907+
}
908+
self
909+
}
910+
911+
#[cfg(target_os = "macos")]
912+
fn hidden_title(mut self, hidden: bool) -> Self {
913+
self.inner = self.inner.with_title_hidden(hidden);
914+
self
915+
}
916+
882917
fn icon(mut self, icon: Icon) -> Result<Self> {
883918
self.inner = self
884919
.inner
@@ -2878,10 +2913,6 @@ fn create_webview<T: UserEvent>(
28782913

28792914
let window_event_listeners = WindowEventListeners::default();
28802915

2881-
#[cfg(target_os = "macos")]
2882-
{
2883-
window_builder.inner = window_builder.inner.with_fullsize_content_view(true);
2884-
}
28852916
#[cfg(windows)]
28862917
{
28872918
window_builder.inner = window_builder

Diff for: core/tauri-runtime/src/webview.rs

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
77
use crate::{menu::Menu, window::DetachedWindow, Icon};
88

9+
#[cfg(target_os = "macos")]
10+
use tauri_utils::TitleBarStyle;
911
use tauri_utils::{
1012
config::{WindowConfig, WindowUrl},
1113
Theme,
@@ -189,6 +191,16 @@ pub trait WindowBuilder: WindowBuilderBase {
189191
#[must_use]
190192
fn owner_window(self, owner: HWND) -> Self;
191193

194+
/// Hide the titlebar. Titlebar buttons will still be visible.
195+
#[cfg(target_os = "macos")]
196+
#[must_use]
197+
fn title_bar_style(self, style: TitleBarStyle) -> Self;
198+
199+
/// Hide the window title.
200+
#[cfg(target_os = "macos")]
201+
#[must_use]
202+
fn hidden_title(self, hidden: bool) -> Self;
203+
192204
/// Forces a theme or uses the system settings if None was provided.
193205
fn theme(self, theme: Option<Theme>) -> Self;
194206

Diff for: core/tauri-utils/src/config.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ use std::{
3434
/// Items to help with parsing content into a [`Config`].
3535
pub mod parse;
3636

37+
use crate::TitleBarStyle;
38+
3739
pub use self::parse::parse;
3840

3941
/// An URL to open on a Tauri webview window.
@@ -859,6 +861,12 @@ pub struct WindowConfig {
859861
pub skip_taskbar: bool,
860862
/// The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.
861863
pub theme: Option<crate::Theme>,
864+
/// The style of the macOS title bar.
865+
#[serde(default, alias = "title-bar-style")]
866+
pub title_bar_style: TitleBarStyle,
867+
/// If `true`, sets the window title to be hidden on macOS.
868+
#[serde(default, alias = "hidden-title")]
869+
pub hidden_title: bool,
862870
}
863871

864872
impl Default for WindowConfig {
@@ -887,6 +895,8 @@ impl Default for WindowConfig {
887895
always_on_top: false,
888896
skip_taskbar: false,
889897
theme: None,
898+
title_bar_style: Default::default(),
899+
hidden_title: false,
890900
}
891901
}
892902
}
@@ -2934,6 +2944,18 @@ mod build {
29342944
}
29352945
}
29362946

2947+
impl ToTokens for crate::TitleBarStyle {
2948+
fn to_tokens(&self, tokens: &mut TokenStream) {
2949+
let prefix = quote! { ::tauri::utils::TitleBarStyle };
2950+
2951+
tokens.append_all(match self {
2952+
Self::Visible => quote! { #prefix::Visible },
2953+
Self::Transparent => quote! { #prefix::Transparent },
2954+
Self::Overlay => quote! { #prefix::Overlay },
2955+
})
2956+
}
2957+
}
2958+
29372959
impl ToTokens for WindowConfig {
29382960
fn to_tokens(&self, tokens: &mut TokenStream) {
29392961
let label = str_lit(&self.label);
@@ -2959,6 +2981,8 @@ mod build {
29592981
let always_on_top = self.always_on_top;
29602982
let skip_taskbar = self.skip_taskbar;
29612983
let theme = opt_lit(self.theme.as_ref());
2984+
let title_bar_style = &self.title_bar_style;
2985+
let hidden_title = self.hidden_title;
29622986

29632987
literal_struct!(
29642988
tokens,
@@ -2985,7 +3009,9 @@ mod build {
29853009
decorations,
29863010
always_on_top,
29873011
skip_taskbar,
2988-
theme
3012+
theme,
3013+
title_bar_style,
3014+
hidden_title
29893015
);
29903016
}
29913017
}

Diff for: core/tauri-utils/src/lib.rs

+62
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,68 @@ impl PackageInfo {
5050
}
5151
}
5252

53+
/// How the window title bar should be displayed.
54+
#[derive(Debug, Clone, PartialEq, Eq)]
55+
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
56+
pub enum TitleBarStyle {
57+
/// A normal title bar.
58+
Visible,
59+
/// Makes the title bar transparent, so the window background color is shown instead.
60+
///
61+
/// Useful if you don't need to have actual HTML under the title bar. This lets you avoid the caveats of using `TitleBarStyle::Overlay`. Will be more useful when Tauri lets you set a custom window background color.
62+
Transparent,
63+
/// Shows the title bar as a transparent overlay over the window's content.
64+
///
65+
/// Keep in mind:
66+
/// - The height of the title bar is different on different OS versions, which can lead to window the controls and title not being where you don't expect.
67+
/// - You need to define a custom drag region to make your window draggable, however due to a limitation you can't drag the window when it's not in focus (https://github.com/tauri-apps/tauri/issues/4316).
68+
/// - The color of the window title depends on the system theme.
69+
Overlay,
70+
}
71+
72+
impl Default for TitleBarStyle {
73+
fn default() -> Self {
74+
Self::Visible
75+
}
76+
}
77+
78+
impl Serialize for TitleBarStyle {
79+
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
80+
where
81+
S: Serializer,
82+
{
83+
serializer.serialize_str(self.to_string().as_ref())
84+
}
85+
}
86+
87+
impl<'de> Deserialize<'de> for TitleBarStyle {
88+
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
89+
where
90+
D: Deserializer<'de>,
91+
{
92+
let s = String::deserialize(deserializer)?;
93+
Ok(match s.to_lowercase().as_str() {
94+
"transparent" => Self::Transparent,
95+
"overlay" => Self::Overlay,
96+
_ => Self::Visible,
97+
})
98+
}
99+
}
100+
101+
impl Display for TitleBarStyle {
102+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103+
write!(
104+
f,
105+
"{}",
106+
match self {
107+
Self::Visible => "Visible",
108+
Self::Transparent => "Transparent",
109+
Self::Overlay => "Overlay",
110+
}
111+
)
112+
}
113+
}
114+
53115
/// System theme.
54116
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
55117
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]

Diff for: core/tauri/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ pub use runtime::http;
206206
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
207207
pub use runtime::{menu::NativeImage, ActivationPolicy};
208208

209+
#[cfg(target_os = "macos")]
210+
pub use self::utils::TitleBarStyle;
209211
#[cfg(all(desktop, feature = "system-tray"))]
210212
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
211213
pub use {

Diff for: core/tauri/src/test/mock_runtime.rs

+12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use tauri_runtime::{
2020
menu::{SystemTrayMenu, TrayHandle},
2121
SystemTray, SystemTrayEvent, TrayId,
2222
};
23+
#[cfg(target_os = "macos")]
24+
use tauri_utils::TitleBarStyle;
2325
use tauri_utils::{config::WindowConfig, Theme};
2426
use uuid::Uuid;
2527

@@ -259,6 +261,16 @@ impl WindowBuilder for MockWindowBuilder {
259261
self
260262
}
261263

264+
#[cfg(target_os = "macos")]
265+
fn title_bar_style(self, style: TitleBarStyle) -> Self {
266+
self
267+
}
268+
269+
#[cfg(target_os = "macos")]
270+
fn hidden_title(self, transparent: bool) -> Self {
271+
self
272+
}
273+
262274
fn theme(self, theme: Option<Theme>) -> Self {
263275
self
264276
}

Diff for: core/tauri/src/window.rs

+18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub(crate) mod menu;
88

99
pub use menu::{MenuEvent, MenuHandle};
1010

11+
#[cfg(target_os = "macos")]
12+
use crate::TitleBarStyle;
1113
use crate::{
1214
app::AppHandle,
1315
command::{CommandArg, CommandItem},
@@ -433,6 +435,22 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
433435
self
434436
}
435437

438+
/// Sets the [`TitleBarStyle`].
439+
#[cfg(target_os = "macos")]
440+
#[must_use]
441+
pub fn title_bar_style(mut self, style: TitleBarStyle) -> Self {
442+
self.window_builder = self.window_builder.title_bar_style(style);
443+
self
444+
}
445+
446+
/// Hide the window title.
447+
#[cfg(target_os = "macos")]
448+
#[must_use]
449+
pub fn hidden_title(mut self, hidden: bool) -> Self {
450+
self.window_builder = self.window_builder.hidden_title(hidden);
451+
self
452+
}
453+
436454
// ------------------------------------------- Webview attributes -------------------------------------------
437455

438456
/// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created,

Diff for: tooling/api/src/window.ts

+9
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import { emit, Event, listen, once } from './helpers/event'
6767
import { TauriEvent } from './event'
6868

6969
type Theme = 'light' | 'dark'
70+
type TitleBarStyle = 'visible' | 'transparent' | 'overlay'
7071

7172
/**
7273
* Allows you to retrieve information about a given monitor.
@@ -2033,6 +2034,14 @@ interface WindowOptions {
20332034
* Only implemented on Windows and macOS 10.14+.
20342035
*/
20352036
theme?: Theme
2037+
/**
2038+
* The style of the macOS title bar.
2039+
*/
2040+
titleBarStyle?: TitleBarStyle
2041+
/**
2042+
* If `true`, sets the window title to be hidden on macOS.
2043+
*/
2044+
hiddenTitle?: boolean
20362045
}
20372046

20382047
function mapMonitor(m: Monitor | null): Monitor | null {

Diff for: tooling/cli/schema.json

+23
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,20 @@
639639
"type": "null"
640640
}
641641
]
642+
},
643+
"titleBarStyle": {
644+
"description": "The style of the macOS title bar.",
645+
"default": "Visible",
646+
"allOf": [
647+
{
648+
"$ref": "#/definitions/TitleBarStyle"
649+
}
650+
]
651+
},
652+
"hiddenTitle": {
653+
"description": "Sets the window title to be hidden on macOS.",
654+
"default": false,
655+
"type": "boolean"
642656
}
643657
},
644658
"additionalProperties": false
@@ -665,6 +679,15 @@
665679
"Dark"
666680
]
667681
},
682+
"TitleBarStyle": {
683+
"description": "How the window title bar should be displayed.",
684+
"type": "string",
685+
"enum": [
686+
"Visible",
687+
"Transparent",
688+
"Overlay"
689+
]
690+
},
668691
"CliConfig": {
669692
"description": "describes a CLI configuration",
670693
"type": "object",

0 commit comments

Comments
 (0)