feat: add liquid glass effects

This commit is contained in:
FabianLars 2025-11-12 14:46:36 +01:00
parent 236f55b7aa
commit d296f21da7
No known key found for this signature in database
13 changed files with 218 additions and 83 deletions

View File

@ -0,0 +1,7 @@
---
tauri: minor:enhance
tauri-utils: minor:enhance
@tauri-apps/api: minor:enhance
---
Added `Regular` and `Clear` Liquid Glass window effects.

175
Cargo.lock generated
View File

@ -815,11 +815,11 @@ dependencies = [
[[package]]
name = "block2"
version = "0.6.0"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037"
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
dependencies = [
"objc2 0.6.0",
"objc2 0.6.3",
]
[[package]]
@ -1987,6 +1987,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dispatch2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.3",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
@ -4643,10 +4653,10 @@ dependencies = [
"gtk",
"keyboard-types",
"libxdo",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"once_cell",
"png",
"serde",
@ -5049,9 +5059,9 @@ dependencies = [
[[package]]
name = "objc2"
version = "0.6.0"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59"
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
dependencies = [
"objc2-encode",
"objc2-exception-helper",
@ -5059,75 +5069,104 @@ dependencies = [
[[package]]
name = "objc2-app-kit"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb"
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
dependencies = [
"bitflags 2.7.0",
"block2 0.6.0",
"block2 0.6.2",
"libc",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-cloud-kit",
"objc2-core-data",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-core-image",
"objc2-foundation 0.3.0",
"objc2-quartz-core 0.3.0",
"objc2-core-text",
"objc2-core-video",
"objc2-foundation 0.3.2",
"objc2-quartz-core 0.3.2",
]
[[package]]
name = "objc2-cloud-kit"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c"
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"objc2-foundation 0.3.0",
"objc2 0.6.3",
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-core-data"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4"
checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"objc2-foundation 0.3.0",
"objc2 0.6.3",
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"dispatch2",
"objc2 0.6.3",
]
[[package]]
name = "objc2-core-graphics"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02"
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"dispatch2",
"objc2 0.6.3",
"objc2-core-foundation",
"objc2-io-surface",
]
[[package]]
name = "objc2-core-image"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56"
checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
dependencies = [
"objc2 0.6.0",
"objc2-foundation 0.3.0",
"objc2 0.6.3",
"objc2-foundation 0.3.2",
]
[[package]]
name = "objc2-core-text"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.3",
"objc2-core-foundation",
"objc2-core-graphics",
]
[[package]]
name = "objc2-core-video"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.3",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-io-surface",
]
[[package]]
@ -5159,25 +5198,25 @@ dependencies = [
[[package]]
name = "objc2-foundation"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998"
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [
"bitflags 2.7.0",
"block2 0.6.0",
"block2 0.6.2",
"libc",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-core-foundation",
]
[[package]]
name = "objc2-io-surface"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19"
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-core-foundation",
]
@ -5208,13 +5247,13 @@ dependencies = [
[[package]]
name = "objc2-quartz-core"
version = "0.3.0"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071"
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"objc2-foundation 0.3.0",
"objc2 0.6.3",
"objc2-foundation 0.3.2",
]
[[package]]
@ -5224,7 +5263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3126341c65c5d5728423ae95d788e1b660756486ad0592307ab87ba02d9a7268"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-core-foundation",
]
@ -5235,9 +5274,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1"
dependencies = [
"bitflags 2.7.0",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-core-foundation",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
]
[[package]]
@ -5247,11 +5286,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce"
dependencies = [
"bitflags 2.7.0",
"block2 0.6.0",
"objc2 0.6.0",
"block2 0.6.2",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"objc2-security",
]
@ -8425,7 +8464,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
dependencies = [
"bitflags 2.7.0",
"block2 0.6.0",
"block2 0.6.2",
"core-foundation 0.10.0",
"core-graphics",
"crossbeam-channel",
@ -8442,9 +8481,9 @@ dependencies = [
"ndk",
"ndk-context",
"ndk-sys",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"once_cell",
"parking_lot",
"raw-window-handle",
@ -8516,9 +8555,9 @@ dependencies = [
"log",
"mime",
"muda",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"objc2-ui-kit",
"objc2-web-kit",
"percent-encoding",
@ -8837,8 +8876,8 @@ dependencies = [
"byte-unit",
"fern",
"log",
"objc2 0.6.0",
"objc2-foundation 0.3.0",
"objc2 0.6.3",
"objc2-foundation 0.3.2",
"serde",
"serde_json",
"serde_repr",
@ -8869,7 +8908,7 @@ dependencies = [
"gtk",
"http 1.3.1",
"jni 0.21.1",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-ui-kit",
"objc2-web-kit",
"raw-window-handle",
@ -8891,9 +8930,9 @@ dependencies = [
"http 1.3.1",
"jni 0.21.1",
"log",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"once_cell",
"percent-encoding",
"raw-window-handle",
@ -9493,11 +9532,11 @@ dependencies = [
"dirs 6.0.0",
"libappindicator",
"muda",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"once_cell",
"png",
"serde",
@ -10335,16 +10374,16 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "window-vibrancy"
version = "0.6.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c"
checksum = "3a9605566982065e4ca0f83c34724385b585d0afc60ab018a8faf6da601b242f"
dependencies = [
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"raw-window-handle",
"windows-sys 0.59.0",
"windows-sys 0.60.2",
"windows-version",
]
@ -10961,7 +11000,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d78ec082b80fa088569a970d043bb3050abaabf4454101d44514ee8d9a8c9f6"
dependencies = [
"base64 0.22.1",
"block2 0.6.0",
"block2 0.6.2",
"cookie",
"crossbeam-channel",
"dirs 6.0.0",
@ -10976,10 +11015,10 @@ dependencies = [
"kuchikiki",
"libc",
"ndk",
"objc2 0.6.0",
"objc2 0.6.3",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation 0.3.0",
"objc2-foundation 0.3.2",
"objc2-ui-kit",
"objc2-web-kit",
"once_cell",

View File

@ -740,7 +740,7 @@
],
"properties": {
"effects": {
"description": "List of Window effects to apply to the Window.\n Conflicting effects will apply the first one and ignore the rest.",
"description": "List of Window effects to apply to the Window.\n\n Generally, conflicting effects will apply the first one and ignore the rest but\n on macOS you can specify one Liquid Glass style and one Visual Effect material at the same time\n to make Tauri fallback to the latter on macOS 15 and below.",
"type": "array",
"items": {
"$ref": "#/definitions/WindowEffect"
@ -766,7 +766,7 @@
"format": "double"
},
"color": {
"description": "Window effect color. Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only\n on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.",
"description": "Window effect color.\n\n ## Platform-specific\n\n - **Windows**: Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only\n on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.\n - **macOS**: Only affects Liquid Glass effects.",
"anyOf": [
{
"$ref": "#/definitions/Color"
@ -920,6 +920,20 @@
"underPageBackground"
]
},
{
"description": "*macOS 26.0+**",
"type": "string",
"enum": [
"liquidGlassRegular"
]
},
{
"description": "*macOS 26.0+**",
"type": "string",
"enum": [
"liquidGlassClear"
]
},
{
"description": "Mica effect that matches the system dark preference **Windows 11 Only**",
"type": "string",

View File

@ -740,7 +740,7 @@
],
"properties": {
"effects": {
"description": "List of Window effects to apply to the Window.\n Conflicting effects will apply the first one and ignore the rest.",
"description": "List of Window effects to apply to the Window.\n\n Generally, conflicting effects will apply the first one and ignore the rest but\n on macOS you can specify one Liquid Glass style and one Visual Effect material at the same time\n to make Tauri fallback to the latter on macOS 15 and below.",
"type": "array",
"items": {
"$ref": "#/definitions/WindowEffect"
@ -766,7 +766,7 @@
"format": "double"
},
"color": {
"description": "Window effect color. Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only\n on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.",
"description": "Window effect color.\n\n ## Platform-specific\n\n - **Windows**: Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only\n on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.\n - **macOS**: Only affects Liquid Glass effects.",
"anyOf": [
{
"$ref": "#/definitions/Color"
@ -920,6 +920,20 @@
"underPageBackground"
]
},
{
"description": "*macOS 26.0+**",
"type": "string",
"enum": [
"liquidGlassRegular"
]
},
{
"description": "*macOS 26.0+**",
"type": "string",
"enum": [
"liquidGlassClear"
]
},
{
"description": "Mica effect that matches the system dark preference **Windows 11 Only**",
"type": "string",

View File

@ -1576,14 +1576,22 @@ pub enum BackgroundThrottlingPolicy {
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WindowEffectsConfig {
/// List of Window effects to apply to the Window.
/// Conflicting effects will apply the first one and ignore the rest.
///
/// Generally, conflicting effects will apply the first one and ignore the rest but
/// on macOS you can specify one Liquid Glass style and one Visual Effect material at the same time
/// to make Tauri fallback to the latter on macOS 15 and below.
pub effects: Vec<WindowEffect>,
/// Window effect state **macOS Only**
pub state: Option<WindowEffectState>,
/// Window effect corner radius **macOS Only**
pub radius: Option<f64>,
/// Window effect color. Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only
/// Window effect color.
///
/// ## Platform-specific
///
/// - **Windows**: Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only
/// on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.
/// - **macOS**: Only affects Liquid Glass effects.
pub color: Option<Color>,
}
@ -3453,6 +3461,8 @@ mod build {
WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
WindowEffect::LiquidGlassRegular => quote! { #prefix::LiquidGlassRegular },
WindowEffect::LiquidGlassClear => quote! { #prefix::LiquidGlassClear },
WindowEffect::Mica => quote! { #prefix::Mica},
WindowEffect::MicaDark => quote! { #prefix::MicaDark},
WindowEffect::MicaLight => quote! { #prefix::MicaLight},

View File

@ -112,6 +112,10 @@ mod window_effects {
UnderWindowBackground,
/// **macOS 10.14+**
UnderPageBackground,
/// *macOS 26.0+**
LiquidGlassRegular,
/// *macOS 26.0+**
LiquidGlassClear,
/// Mica effect that matches the system dark preference **Windows 11 Only**
Mica,
/// Mica effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**

View File

@ -126,12 +126,12 @@ objc2-web-kit = { version = "0.3", features = [
"WKWebView",
"WKUserContentController",
] }
window-vibrancy = "0.6"
window-vibrancy = "0.7"
# windows
[target."cfg(windows)".dependencies]
webview2-com = { version = "0.38", optional = true }
window-vibrancy = "0.6"
window-vibrancy = "0.7"
windows = { version = "0.61", features = [
"Win32_Foundation",
"Win32_UI",

File diff suppressed because one or more lines are too long

View File

@ -6,16 +6,39 @@
use crate::utils::config::WindowEffectsConfig;
use crate::window::{Effect, EffectState};
use objc2_app_kit::NSAppKitVersionNumber;
use raw_window_handle::HasWindowHandle;
use window_vibrancy::{NSVisualEffectMaterial, NSVisualEffectState};
use window_vibrancy::{
clear_liquid_glass, NSGlassEffectViewStyle, NSVisualEffectMaterial, NSVisualEffectState,
};
pub fn apply_effects(window: impl HasWindowHandle, effects: WindowEffectsConfig) {
let WindowEffectsConfig {
effects,
radius,
state,
..
color,
} = effects;
if unsafe { NSAppKitVersionNumber } >= 2685.0 {
if let Some(effect) = effects
.iter()
.find(|e| matches!(e, Effect::LiquidGlassRegular | Effect::LiquidGlassClear))
{
window_vibrancy::apply_liquid_glass(
window,
match effect {
Effect::LiquidGlassRegular => NSGlassEffectViewStyle::Regular,
Effect::LiquidGlassClear => NSGlassEffectViewStyle::Clear,
_ => unreachable!(),
},
color.map(|c| (c.0, c.1, c.2, c.3)),
radius,
);
return;
}
}
let effect = if let Some(effect) = effects.into_iter().find(|e| {
matches!(
e,
@ -77,3 +100,7 @@ pub fn apply_effects(window: impl HasWindowHandle, effects: WindowEffectsConfig)
radius,
);
}
pub fn clear_effects(window: impl HasWindowHandle) {
let _ = clear_liquid_glass(&window);
}

View File

@ -25,6 +25,8 @@ pub fn set_window_effects<R: Runtime>(
} else {
#[cfg(windows)]
windows::clear_effects(window);
#[cfg(target_os = "macos")]
macos::clear_effects(window);
}
Ok(())
}

View File

@ -1845,6 +1845,7 @@ tauri::Builder::default()
/// ## Platform-specific:
///
/// - **Windows**: If using decorations or shadows, you may want to try this workaround <https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891>
/// - **macOS**: Using `None` can only clear Liquid Glass effects, not Visual Effects.
/// - **Linux**: Unsupported
pub fn set_effects<E: Into<Option<WindowEffectsConfig>>>(&self, effects: E) -> crate::Result<()> {
let effects = effects.into();

View File

@ -80,6 +80,7 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
.title("Tauri API Validation")
.inner_size(1000., 800.)
.min_inner_size(600., 400.)
.transparent(true)
.menu(tauri::menu::Menu::default(app.handle())?)
.on_new_window(move |url, features| {
println!("new window requested: {url:?} {features:?}");

View File

@ -2208,6 +2208,14 @@ enum Effect {
* **macOS 10.14+**
*/
UnderPageBackground = 'underPageBackground',
/**
* **macOS 10.14+**
*/
LiquidGlassRegular = 'liquidGlassRegular',
/**
* **macOS 10.14+**
*/
LiquidGlassClear = 'liquidGlassClear',
/**
* **Windows 11 Only**
*/
@ -2270,12 +2278,15 @@ enum EffectState {
*/
interface Effects {
/**
* List of Window effects to apply to the Window.
* Conflicting effects will apply the first one and ignore the rest.
* List of Window effects to apply to the Window.
*
* Generally, conflicting effects will apply the first one and ignore the rest but
* on macOS you can specify one Liquid Glass style and one Visual Effect material at the same time
* to make Tauri fallback to the latter on macOS 15 and below.
*/
effects: Effect[]
/**
* Window effect state **macOS Only**
* Window effect state **macOS Only**. Ignored for Liquid Glass Effects.
*/
state?: EffectState
/**
@ -2283,8 +2294,13 @@ interface Effects {
*/
radius?: number
/**
* Window effect color. Affects {@link Effect.Blur} and {@link Effect.Acrylic} only
* Window effect color.
*
* #### Platform-specific
*
* - **Windows**: Affects {@link Effect.Blur} and {@link Effect.Acrylic} only
* on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.
* - **macOS**: Only affects Liquid Glass effects.
*/
color?: Color
}