浏览代码

feat: add window effects api (#6442)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Amr Bashir 2 年之前
父节点
当前提交
e0f0dce220

+ 5 - 0
.changes/window-effects-api.md

@@ -0,0 +1,5 @@
+---
+'api': minor
+---
+
+Added the `windowEffects` option when creating a window and `setWindowEffects` method to change it at runtime.

+ 5 - 0
.changes/window-effects-config.md

@@ -0,0 +1,5 @@
+---
+'tauri-utils': minor
+---
+
+Added the `window_effects` option to the window configuration.

+ 7 - 0
.changes/window-effects.md

@@ -0,0 +1,7 @@
+---
+'tauri': minor
+'tauri-runtime-wry': minor
+'tauri-runtime': minor
+---
+
+Added the `window_effects` option when creating a window and `Window::set_effects` to change it at runtime.

+ 276 - 0
core/tauri-config-schema/schema.json

@@ -488,6 +488,17 @@
           "description": "Whether or not the window has shadow.\n\n## Platform-specific\n\n- **Windows:** - `false` has no effect on decorated window, shadow are always ON. - `true` will make ndecorated window have a 1px white border, and on Windows 11, it will have a rounded corners. - **Linux:** Unsupported.",
           "default": true,
           "type": "boolean"
+        },
+        "windowEffects": {
+          "description": "Window effects.\n\nRequires the window to be transparent.\n\n## Platform-specific:\n\n- **Windows**: If using decorations or shadows, you may want to try this workaround https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891 - **Linux**: Unsupported",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/WindowEffectsConfig"
+            },
+            {
+              "type": "null"
+            }
+          ]
         }
       },
       "additionalProperties": false
@@ -551,6 +562,271 @@
         }
       ]
     },
+    "WindowEffectsConfig": {
+      "description": "The window effects configuration object",
+      "type": "object",
+      "required": [
+        "effects"
+      ],
+      "properties": {
+        "effects": {
+          "description": "List of Window effects to apply to the Window. Conflicting effects will apply the first one and ignore the rest.",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/WindowEffect"
+          }
+        },
+        "state": {
+          "description": "Window effect state **macOS Only**",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/WindowEffectState"
+            },
+            {
+              "type": "null"
+            }
+          ]
+        },
+        "radius": {
+          "description": "Window effect corner radius **macOS Only**",
+          "type": [
+            "number",
+            "null"
+          ],
+          "format": "double"
+        },
+        "color": {
+          "description": "Window effect color. Affects [`WindowEffects::Blur`] and [`WindowEffects::Acrylic`] only on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/Color"
+            },
+            {
+              "type": "null"
+            }
+          ]
+        }
+      },
+      "additionalProperties": false
+    },
+    "WindowEffect": {
+      "description": "Platform-specific window effects",
+      "oneOf": [
+        {
+          "description": "A default material appropriate for the view's effectiveAppearance. **macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "appearanceBased"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "light"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "dark"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "mediumLight"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "ultraDark"
+          ]
+        },
+        {
+          "description": "*macOS 10.10+**",
+          "type": "string",
+          "enum": [
+            "titlebar"
+          ]
+        },
+        {
+          "description": "*macOS 10.10+**",
+          "type": "string",
+          "enum": [
+            "selection"
+          ]
+        },
+        {
+          "description": "*macOS 10.11+**",
+          "type": "string",
+          "enum": [
+            "menu"
+          ]
+        },
+        {
+          "description": "*macOS 10.11+**",
+          "type": "string",
+          "enum": [
+            "popover"
+          ]
+        },
+        {
+          "description": "*macOS 10.11+**",
+          "type": "string",
+          "enum": [
+            "sidebar"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "headerView"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "sheet"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "windowBackground"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "hudWindow"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "fullScreenUI"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "tooltip"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "contentBackground"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "underWindowBackground"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "underPageBackground"
+          ]
+        },
+        {
+          "description": "*Windows 11 Only**",
+          "type": "string",
+          "enum": [
+            "mica"
+          ]
+        },
+        {
+          "description": "**Windows 7/10/11(22H1) Only**\n\n## Notes\n\nThis effect has bad performance when resizing/dragging the window on Windows 11 build 22621.",
+          "type": "string",
+          "enum": [
+            "blur"
+          ]
+        },
+        {
+          "description": "**Windows 10/11 Only**\n\n## Notes\n\nThis effect has bad performance when resizing/dragging the window on Windows 10 v1903+ and Windows 11 build 22000.",
+          "type": "string",
+          "enum": [
+            "acrylic"
+          ]
+        }
+      ]
+    },
+    "WindowEffectState": {
+      "description": "Window effect state **macOS only**\n\n<https://developer.apple.com/documentation/appkit/nsvisualeffectview/state>",
+      "oneOf": [
+        {
+          "description": "Make window effect state follow the window's active state",
+          "type": "string",
+          "enum": [
+            "followsWindowActiveState"
+          ]
+        },
+        {
+          "description": "Make window effect state always active",
+          "type": "string",
+          "enum": [
+            "active"
+          ]
+        },
+        {
+          "description": "Make window effect state always inactive",
+          "type": "string",
+          "enum": [
+            "inactive"
+          ]
+        }
+      ]
+    },
+    "Color": {
+      "description": "a tuple struct of RGBA colors. Each value has minimum of 0 and maximum of 255.",
+      "type": "array",
+      "items": [
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        },
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        },
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        },
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        }
+      ],
+      "maxItems": 4,
+      "minItems": 4
+    },
     "BundleConfig": {
       "description": "Configuration for tauri-bundler.\n\nSee more: https://tauri.app/v1/api/config#bundleconfig",
       "type": "object",

+ 10 - 1
core/tauri-runtime/src/webview.rs

@@ -9,7 +9,7 @@ use crate::{menu::Menu, window::DetachedWindow, Icon};
 #[cfg(target_os = "macos")]
 use tauri_utils::TitleBarStyle;
 use tauri_utils::{
-  config::{WindowConfig, WindowUrl},
+  config::{WindowConfig, WindowEffectsConfig, WindowUrl},
   Theme,
 };
 
@@ -29,6 +29,7 @@ pub struct WebviewAttributes {
   pub clipboard: bool,
   pub accept_first_mouse: bool,
   pub additional_browser_args: Option<String>,
+  pub window_effects: Option<WindowEffectsConfig>,
 }
 
 impl WebviewAttributes {
@@ -43,6 +44,7 @@ impl WebviewAttributes {
       clipboard: false,
       accept_first_mouse: false,
       additional_browser_args: None,
+      window_effects: None,
     }
   }
 
@@ -97,6 +99,13 @@ impl WebviewAttributes {
     self.additional_browser_args = Some(additional_args.to_string());
     self
   }
+
+  /// Sets window effects
+  #[must_use]
+  pub fn window_effects(mut self, effects: WindowEffectsConfig) -> Self {
+    self.window_effects = Some(effects);
+    self
+  }
 }
 
 /// Do **NOT** implement this trait except for use in a custom [`Runtime`](crate::Runtime).

+ 102 - 3
core/tauri-utils/src/config.rs

@@ -34,7 +34,7 @@ use std::{
 /// Items to help with parsing content into a [`Config`].
 pub mod parse;
 
-use crate::TitleBarStyle;
+use crate::{TitleBarStyle, WindowEffect, WindowEffectState};
 
 pub use self::parse::parse;
 
@@ -747,6 +747,30 @@ pub struct BundleConfig {
   pub updater: UpdaterConfig,
 }
 
+/// a tuple struct of RGBA colors. Each value has minimum of 0 and maximum of 255.
+#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct Color(pub u8, pub u8, pub u8, pub u8);
+
+/// The window effects configuration object
+#[skip_serializing_none]
+#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[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.
+  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 [`WindowEffects::Blur`] and [`WindowEffects::Acrylic`] only
+  /// on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.
+  pub color: Option<Color>,
+}
+
 /// The window configuration object.
 ///
 /// See more: https://tauri.app/v1/api/config#windowconfig
@@ -864,6 +888,16 @@ pub struct WindowConfig {
   /// - **Linux:** Unsupported.
   #[serde(default = "default_true")]
   pub shadow: bool,
+  /// Window effects.
+  ///
+  /// Requires the window to be transparent.
+  ///
+  /// ## 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
+  /// - **Linux**: Unsupported
+  #[serde(default, alias = "window-effects")]
+  pub window_effects: Option<WindowEffectsConfig>,
 }
 
 impl Default for WindowConfig {
@@ -900,6 +934,7 @@ impl Default for WindowConfig {
       tabbing_identifier: None,
       additional_browser_args: None,
       shadow: true,
+      window_effects: None,
     }
   }
 }
@@ -1924,7 +1959,7 @@ mod build {
         ::tauri::utils::config::$struct {
           $($field: #$field),+
         }
-      });
+      })
     };
   }
 
@@ -1956,6 +1991,23 @@ mod build {
     }
   }
 
+  impl ToTokens for Color {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+      let Color(r, g, b, a) = self;
+      tokens.append_all(quote! {::tauri::utils::Color(#r,#g,#b,#a)});
+    }
+  }
+  impl ToTokens for WindowEffectsConfig {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+      let effects = vec_lit(self.effects.clone(), |d| d);
+      let state = opt_lit(self.state.as_ref());
+      let radius = opt_lit(self.radius.as_ref());
+      let color = opt_lit(self.color.as_ref());
+
+      literal_struct!(tokens, WindowEffectsConfig, effects, state, radius, color)
+    }
+  }
+
   impl ToTokens for crate::TitleBarStyle {
     fn to_tokens(&self, tokens: &mut TokenStream) {
       let prefix = quote! { ::tauri::utils::TitleBarStyle };
@@ -1968,6 +2020,51 @@ mod build {
     }
   }
 
+  impl ToTokens for crate::WindowEffect {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+      let prefix = quote! { ::tauri::utils::WindowEffect };
+
+      #[allow(deprecated)]
+      tokens.append_all(match self {
+        WindowEffect::AppearanceBased => quote! { #prefix::AppearanceBased},
+        WindowEffect::Light => quote! { #prefix::Light},
+        WindowEffect::Dark => quote! { #prefix::Dark},
+        WindowEffect::MediumLight => quote! { #prefix::MediumLight},
+        WindowEffect::UltraDark => quote! { #prefix::UltraDark},
+        WindowEffect::Titlebar => quote! { #prefix::Titlebar},
+        WindowEffect::Selection => quote! { #prefix::Selection},
+        WindowEffect::Menu => quote! { #prefix::Menu},
+        WindowEffect::Popover => quote! { #prefix::Popover},
+        WindowEffect::Sidebar => quote! { #prefix::Sidebar},
+        WindowEffect::HeaderView => quote! { #prefix::HeaderView},
+        WindowEffect::Sheet => quote! { #prefix::Sheet},
+        WindowEffect::WindowBackground => quote! { #prefix::WindowBackground},
+        WindowEffect::HudWindow => quote! { #prefix::HudWindow},
+        WindowEffect::FullScreenUI => quote! { #prefix::FullScreenUI},
+        WindowEffect::Tooltip => quote! { #prefix::Tooltip},
+        WindowEffect::ContentBackground => quote! { #prefix::ContentBackground},
+        WindowEffect::UnderWindowBackground => quote! { #prefix::UnderWindowBackground},
+        WindowEffect::UnderPageBackground => quote! { #prefix::UnderPageBackground},
+        WindowEffect::Mica => quote! { #prefix::Mica},
+        WindowEffect::Blur => quote! { #prefix::Blur},
+        WindowEffect::Acrylic => quote! { #prefix::Acrylic},
+      })
+    }
+  }
+
+  impl ToTokens for crate::WindowEffectState {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+      let prefix = quote! { ::tauri::utils::WindowEffectState };
+
+      #[allow(deprecated)]
+      tokens.append_all(match self {
+        WindowEffectState::Active => quote! { #prefix::Active},
+        WindowEffectState::FollowsWindowActiveState => quote! { #prefix::FollowsWindowActiveState},
+        WindowEffectState::Inactive => quote! { #prefix::Inactive},
+      })
+    }
+  }
+
   impl ToTokens for WindowConfig {
     fn to_tokens(&self, tokens: &mut TokenStream) {
       let label = str_lit(&self.label);
@@ -2001,6 +2098,7 @@ mod build {
       let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref());
       let additional_browser_args = opt_str_lit(self.additional_browser_args.as_ref());
       let shadow = self.shadow;
+      let window_effects = opt_lit(self.window_effects.as_ref());
 
       literal_struct!(
         tokens,
@@ -2035,7 +2133,8 @@ mod build {
         accept_first_mouse,
         tabbing_identifier,
         additional_browser_args,
-        shadow
+        shadow,
+        window_effects
       );
     }
   }

+ 90 - 0
core/tauri-utils/src/lib.rs

@@ -4,6 +4,7 @@
 
 //! Tauri utility helpers
 #![warn(missing_docs, rust_2018_idioms)]
+#![allow(clippy::deprecated_semver)]
 
 use std::{
   fmt::Display,
@@ -55,6 +56,95 @@ impl PackageInfo {
   }
 }
 
+#[allow(deprecated)]
+mod window_effects {
+  use super::*;
+
+  #[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
+  #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
+  #[serde(rename_all = "camelCase")]
+  /// Platform-specific window effects
+  pub enum WindowEffect {
+    /// A default material appropriate for the view's effectiveAppearance. **macOS 10.14-**
+    #[deprecated(
+      since = "macOS 10.14",
+      note = "You should instead choose an appropriate semantic material."
+    )]
+    AppearanceBased,
+    /// **macOS 10.14-**
+    #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+    Light,
+    /// **macOS 10.14-**
+    #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+    Dark,
+    /// **macOS 10.14-**
+    #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+    MediumLight,
+    /// **macOS 10.14-**
+    #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+    UltraDark,
+    /// **macOS 10.10+**
+    Titlebar,
+    /// **macOS 10.10+**
+    Selection,
+    /// **macOS 10.11+**
+    Menu,
+    /// **macOS 10.11+**
+    Popover,
+    /// **macOS 10.11+**
+    Sidebar,
+    /// **macOS 10.14+**
+    HeaderView,
+    /// **macOS 10.14+**
+    Sheet,
+    /// **macOS 10.14+**
+    WindowBackground,
+    /// **macOS 10.14+**
+    HudWindow,
+    /// **macOS 10.14+**
+    FullScreenUI,
+    /// **macOS 10.14+**
+    Tooltip,
+    /// **macOS 10.14+**
+    ContentBackground,
+    /// **macOS 10.14+**
+    UnderWindowBackground,
+    /// **macOS 10.14+**
+    UnderPageBackground,
+    /// **Windows 11 Only**
+    Mica,
+    /// **Windows 7/10/11(22H1) Only**
+    ///
+    /// ## Notes
+    ///
+    /// This effect has bad performance when resizing/dragging the window on Windows 11 build 22621.
+    Blur,
+    /// **Windows 10/11 Only**
+    ///
+    /// ## Notes
+    ///
+    /// This effect has bad performance when resizing/dragging the window on Windows 10 v1903+ and Windows 11 build 22000.
+    Acrylic,
+  }
+
+  /// Window effect state **macOS only**
+  ///
+  /// <https://developer.apple.com/documentation/appkit/nsvisualeffectview/state>
+  #[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, Serialize)]
+  #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
+  #[serde(rename_all = "camelCase")]
+  pub enum WindowEffectState {
+    /// Make window effect state follow the window's active state
+    FollowsWindowActiveState,
+    /// Make window effect state always active
+    Active,
+    /// Make window effect state always inactive
+    Inactive,
+  }
+}
+
+pub use window_effects::{WindowEffect, WindowEffectState};
+
 /// How the window title bar should be displayed on macOS.
 #[derive(Debug, Clone, PartialEq, Eq)]
 #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]

+ 4 - 4
core/tauri-utils/src/platform.rs

@@ -204,7 +204,7 @@ pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<Path
 }
 
 #[cfg(windows)]
-pub use windows_platform::{is_windows_7, windows_version};
+pub use windows_platform::{get_function_impl, is_windows_7, windows_version};
 
 #[cfg(windows)]
 mod windows_platform {
@@ -235,9 +235,9 @@ mod windows_platform {
     string.as_ref().encode_wide().chain(once(0)).collect()
   }
 
-  // Helper function to dynamically load function pointer.
-  // `library` and `function` must be zero-terminated.
-  fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
+  /// Helper function to dynamically load function pointer.
+  /// `library` and `function` must be null-terminated.
+  pub fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
     let library = encode_wide(library);
     assert_eq!(function.chars().last(), Some('\0'));
     let function = PCSTR::from_raw(function.as_ptr());

+ 10 - 1
core/tauri/src/app.rs

@@ -1289,6 +1289,7 @@ impl<R: Runtime> Builder<R> {
     for config in manager.config().tauri.windows.clone() {
       let url = config.url.clone();
       let label = config.label.clone();
+      let window_effects = config.window_effects.clone();
 
       let mut webview_attributes =
         WebviewAttributes::new(url).accept_first_mouse(config.accept_first_mouse);
@@ -1301,6 +1302,9 @@ impl<R: Runtime> Builder<R> {
       if !config.file_drop_enabled {
         webview_attributes = webview_attributes.disable_file_drop_handler();
       }
+      if let Some(effects) = window_effects {
+        webview_attributes = webview_attributes.window_effects(effects);
+      }
 
       self.pending_windows.push(PendingWindow::with_config(
         config,
@@ -1430,13 +1434,18 @@ fn setup<R: Runtime>(app: &mut App<R>) -> crate::Result<()> {
         app
           .manager
           .prepare_window(app.handle.clone(), pending, &window_labels, None)?;
+      let window_effects = pending.webview_attributes.window_effects.clone();
       let detached = if let RuntimeOrDispatch::RuntimeHandle(runtime) = app.handle().runtime() {
         runtime.create_window(pending)?
       } else {
         // the AppHandle's runtime is always RuntimeOrDispatch::RuntimeHandle
         unreachable!()
       };
-      let _window = app.manager.attach_window(app.handle(), detached);
+      let window = app.manager.attach_window(app.handle(), detached);
+
+      if let Some(effects) = window_effects {
+        crate::vibrancy::set_window_effects(&window, Some(effects))?;
+      }
     }
   }
 

+ 1 - 0
core/tauri/src/lib.rs

@@ -76,6 +76,7 @@ mod hooks;
 mod manager;
 mod pattern;
 pub mod plugin;
+mod vibrancy;
 pub mod window;
 use tauri_runtime as runtime;
 #[cfg(target_os = "ios")]

+ 300 - 0
core/tauri/src/vibrancy/macos.rs

@@ -0,0 +1,300 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+#![cfg(target_os = "macos")]
+#![allow(deprecated)]
+
+use crate::utils::config::WindowEffectsConfig;
+use crate::window::{Effect, EffectState};
+use cocoa::{
+  appkit::{
+    NSAppKitVersionNumber, NSAppKitVersionNumber10_10, NSAppKitVersionNumber10_11,
+    NSAutoresizingMaskOptions, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow,
+    NSWindowOrderingMode,
+  },
+  base::{id, nil, BOOL},
+  foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize},
+};
+use objc::{class, msg_send, sel, sel_impl};
+
+pub fn apply_effects(window: id, effects: WindowEffectsConfig) {
+  let WindowEffectsConfig {
+    effects,
+    radius,
+    state,
+    ..
+  } = effects;
+  let mut appearance: NSVisualEffectMaterial = if let Some(effect) = effects.into_iter().find(|e| {
+    matches!(
+      e,
+      Effect::AppearanceBased
+        | Effect::Light
+        | Effect::Dark
+        | Effect::MediumLight
+        | Effect::UltraDark
+        | Effect::Titlebar
+        | Effect::Selection
+        | Effect::Menu
+        | Effect::Popover
+        | Effect::Sidebar
+        | Effect::HeaderView
+        | Effect::Sheet
+        | Effect::WindowBackground
+        | Effect::HudWindow
+        | Effect::FullScreenUI
+        | Effect::Tooltip
+        | Effect::ContentBackground
+        | Effect::UnderWindowBackground
+        | Effect::UnderPageBackground
+    )
+  }) {
+    effect.into()
+  } else {
+    return;
+  };
+
+  unsafe {
+    if NSAppKitVersionNumber < NSAppKitVersionNumber10_10 {
+      return;
+    }
+
+    if !msg_send![class!(NSThread), isMainThread] {
+      return;
+    }
+
+    if appearance as u32 > 9 && NSAppKitVersionNumber < NSAppKitVersionNumber10_14 {
+      appearance = NSVisualEffectMaterial::AppearanceBased;
+    } else if appearance as u32 > 4 && NSAppKitVersionNumber < NSAppKitVersionNumber10_11 {
+      appearance = NSVisualEffectMaterial::AppearanceBased;
+    }
+
+    let ns_view: id = window.contentView();
+    let bounds = NSView::bounds(ns_view);
+
+    let blurred_view = NSVisualEffectView::initWithFrame_(NSVisualEffectView::alloc(nil), bounds);
+    blurred_view.autorelease();
+
+    blurred_view.setMaterial_(appearance);
+    blurred_view.setCornerRadius_(radius.unwrap_or(0.0));
+    blurred_view.setBlendingMode_(NSVisualEffectBlendingMode::BehindWindow);
+    blurred_view.setState_(
+      state
+        .map(Into::into)
+        .unwrap_or(NSVisualEffectState::FollowsWindowActiveState),
+    );
+    NSVisualEffectView::setAutoresizingMask_(
+      blurred_view,
+      NSViewWidthSizable | NSViewHeightSizable,
+    );
+
+    let _: () = msg_send![ns_view, addSubview: blurred_view positioned: NSWindowOrderingMode::NSWindowBelow relativeTo: 0];
+  }
+}
+
+#[allow(non_upper_case_globals)]
+const NSAppKitVersionNumber10_14: f64 = 1671.0;
+
+// https://developer.apple.com/documentation/appkit/nsvisualeffectview/blendingmode
+#[allow(dead_code)]
+#[repr(u64)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum NSVisualEffectBlendingMode {
+  BehindWindow = 0,
+  WithinWindow = 1,
+}
+
+// macos 10.10+
+// https://developer.apple.com/documentation/appkit/nsvisualeffectview
+#[allow(non_snake_case)]
+trait NSVisualEffectView: Sized {
+  unsafe fn alloc(_: Self) -> id {
+    msg_send![class!(NSVisualEffectView), alloc]
+  }
+
+  unsafe fn init(self) -> id;
+  unsafe fn initWithFrame_(self, frameRect: NSRect) -> id;
+  unsafe fn bounds(self) -> NSRect;
+  unsafe fn frame(self) -> NSRect;
+  unsafe fn setFrameSize(self, frameSize: NSSize);
+  unsafe fn setFrameOrigin(self, frameOrigin: NSPoint);
+
+  unsafe fn superview(self) -> id;
+  unsafe fn removeFromSuperview(self);
+  unsafe fn setAutoresizingMask_(self, autoresizingMask: NSAutoresizingMaskOptions);
+
+  // API_AVAILABLE(macos(10.12));
+  unsafe fn isEmphasized(self) -> BOOL;
+  // API_AVAILABLE(macos(10.12));
+  unsafe fn setEmphasized_(self, emphasized: BOOL);
+
+  unsafe fn setMaterial_(self, material: NSVisualEffectMaterial);
+  unsafe fn setCornerRadius_(self, radius: f64);
+  unsafe fn setState_(self, state: NSVisualEffectState);
+  unsafe fn setBlendingMode_(self, mode: NSVisualEffectBlendingMode);
+}
+
+#[allow(non_snake_case)]
+impl NSVisualEffectView for id {
+  unsafe fn init(self) -> id {
+    msg_send![self, init]
+  }
+
+  unsafe fn initWithFrame_(self, frameRect: NSRect) -> id {
+    msg_send![self, initWithFrame: frameRect]
+  }
+
+  unsafe fn bounds(self) -> NSRect {
+    msg_send![self, bounds]
+  }
+
+  unsafe fn frame(self) -> NSRect {
+    msg_send![self, frame]
+  }
+
+  unsafe fn setFrameSize(self, frameSize: NSSize) {
+    msg_send![self, setFrameSize: frameSize]
+  }
+
+  unsafe fn setFrameOrigin(self, frameOrigin: NSPoint) {
+    msg_send![self, setFrameOrigin: frameOrigin]
+  }
+
+  unsafe fn superview(self) -> id {
+    msg_send![self, superview]
+  }
+
+  unsafe fn removeFromSuperview(self) {
+    msg_send![self, removeFromSuperview]
+  }
+
+  unsafe fn setAutoresizingMask_(self, autoresizingMask: NSAutoresizingMaskOptions) {
+    msg_send![self, setAutoresizingMask: autoresizingMask]
+  }
+
+  // API_AVAILABLE(macos(10.12));
+  unsafe fn isEmphasized(self) -> BOOL {
+    msg_send![self, isEmphasized]
+  }
+
+  // API_AVAILABLE(macos(10.12));
+  unsafe fn setEmphasized_(self, emphasized: BOOL) {
+    msg_send![self, setEmphasized: emphasized]
+  }
+
+  unsafe fn setMaterial_(self, material: NSVisualEffectMaterial) {
+    msg_send![self, setMaterial: material]
+  }
+
+  unsafe fn setCornerRadius_(self, radius: f64) {
+    msg_send![self, setCornerRadius: radius]
+  }
+
+  unsafe fn setState_(self, state: NSVisualEffectState) {
+    msg_send![self, setState: state]
+  }
+
+  unsafe fn setBlendingMode_(self, mode: NSVisualEffectBlendingMode) {
+    msg_send![self, setBlendingMode: mode]
+  }
+}
+
+/// <https://developer.apple.com/documentation/appkit/nsvisualeffectview/material>
+#[repr(u64)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum NSVisualEffectMaterial {
+  #[deprecated(
+    since = "macOS 10.14",
+    note = "A default material appropriate for the view's effectiveAppearance.  You should instead choose an appropriate semantic material."
+  )]
+  AppearanceBased = 0,
+  #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+  Light = 1,
+  #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+  Dark = 2,
+  #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+  MediumLight = 8,
+  #[deprecated(since = "macOS 10.14", note = "Use a semantic material instead.")]
+  UltraDark = 9,
+
+  /// macOS 10.10+
+  Titlebar = 3,
+  /// macOS 10.10+
+  Selection = 4,
+
+  /// macOS 10.11+
+  Menu = 5,
+  /// macOS 10.11+
+  Popover = 6,
+  /// macOS 10.11+
+  Sidebar = 7,
+
+  /// macOS 10.14+
+  HeaderView = 10,
+  /// macOS 10.14+
+  Sheet = 11,
+  /// macOS 10.14+
+  WindowBackground = 12,
+  /// macOS 10.14+
+  HudWindow = 13,
+  /// macOS 10.14+
+  FullScreenUI = 15,
+  /// macOS 10.14+
+  Tooltip = 17,
+  /// macOS 10.14+
+  ContentBackground = 18,
+  /// macOS 10.14+
+  UnderWindowBackground = 21,
+  /// macOS 10.14+
+  UnderPageBackground = 22,
+}
+
+/// <https://developer.apple.com/documentation/appkit/nsvisualeffectview/state>
+#[allow(dead_code)]
+#[repr(u64)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum NSVisualEffectState {
+  /// Make window vibrancy state follow the window's active state
+  FollowsWindowActiveState = 0,
+  /// Make window vibrancy state always active
+  Active = 1,
+  /// Make window vibrancy state always inactive
+  Inactive = 2,
+}
+
+impl From<crate::window::Effect> for NSVisualEffectMaterial {
+  fn from(value: crate::window::Effect) -> Self {
+    match value {
+      Effect::AppearanceBased => NSVisualEffectMaterial::AppearanceBased,
+      Effect::Light => NSVisualEffectMaterial::Light,
+      Effect::Dark => NSVisualEffectMaterial::Dark,
+      Effect::MediumLight => NSVisualEffectMaterial::MediumLight,
+      Effect::UltraDark => NSVisualEffectMaterial::UltraDark,
+      Effect::Titlebar => NSVisualEffectMaterial::Titlebar,
+      Effect::Selection => NSVisualEffectMaterial::Selection,
+      Effect::Menu => NSVisualEffectMaterial::Menu,
+      Effect::Popover => NSVisualEffectMaterial::Popover,
+      Effect::Sidebar => NSVisualEffectMaterial::Sidebar,
+      Effect::HeaderView => NSVisualEffectMaterial::HeaderView,
+      Effect::Sheet => NSVisualEffectMaterial::Sheet,
+      Effect::WindowBackground => NSVisualEffectMaterial::WindowBackground,
+      Effect::HudWindow => NSVisualEffectMaterial::HudWindow,
+      Effect::FullScreenUI => NSVisualEffectMaterial::FullScreenUI,
+      Effect::Tooltip => NSVisualEffectMaterial::Tooltip,
+      Effect::ContentBackground => NSVisualEffectMaterial::ContentBackground,
+      Effect::UnderWindowBackground => NSVisualEffectMaterial::UnderWindowBackground,
+      Effect::UnderPageBackground => NSVisualEffectMaterial::UnderPageBackground,
+      Effect::Mica | Effect::Blur | Effect::Acrylic => unreachable!(),
+    }
+  }
+}
+
+impl From<crate::window::EffectState> for NSVisualEffectState {
+  fn from(value: crate::window::EffectState) -> Self {
+    match value {
+      EffectState::FollowsWindowActiveState => NSVisualEffectState::FollowsWindowActiveState,
+      EffectState::Active => NSVisualEffectState::Active,
+      EffectState::Inactive => NSVisualEffectState::Inactive,
+    }
+  }
+}

+ 39 - 0
core/tauri/src/vibrancy/mod.rs

@@ -0,0 +1,39 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+#![allow(unused)]
+
+use tauri_utils::config::WindowEffectsConfig;
+
+use crate::{Runtime, Window};
+
+#[cfg(target_os = "macos")]
+mod macos;
+#[cfg(windows)]
+mod windows;
+
+pub fn set_window_effects<R: Runtime>(
+  window: &Window<R>,
+  effects: Option<WindowEffectsConfig>,
+) -> crate::Result<()> {
+  if let Some(_effects) = effects {
+    #[cfg(windows)]
+    {
+      let hwnd = window.hwnd()?;
+      windows::apply_effects(hwnd, _effects);
+    }
+    #[cfg(target_os = "macos")]
+    {
+      let ns_window = window.ns_window()?;
+      macos::apply_effects(ns_window as _, _effects);
+    }
+  } else {
+    #[cfg(windows)]
+    {
+      let hwnd = window.hwnd()?;
+      windows::clear_effects(hwnd);
+    }
+  }
+  Ok(())
+}

+ 249 - 0
core/tauri/src/vibrancy/windows.rs

@@ -0,0 +1,249 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+#![cfg(windows)]
+#![allow(non_snake_case)]
+#![allow(non_camel_case_types)]
+#![allow(clippy::upper_case_acronyms)]
+
+use std::ffi::c_void;
+
+use crate::utils::config::WindowEffectsConfig;
+use crate::window::{Color, Effect};
+use tauri_utils::platform::{get_function_impl, is_windows_7, windows_version};
+use windows::Win32::Graphics::Dwm::{DwmSetWindowAttribute, DWMWINDOWATTRIBUTE};
+use windows::Win32::{
+  Foundation::{BOOL, HWND},
+  Graphics::{
+    Dwm::{DwmEnableBlurBehindWindow, DWM_BB_ENABLE, DWM_BLURBEHIND},
+    Gdi::HRGN,
+  },
+};
+
+pub fn apply_effects(window: HWND, effects: WindowEffectsConfig) {
+  let WindowEffectsConfig { effects, color, .. } = effects;
+  let effect = if let Some(effect) = effects
+    .iter()
+    .find(|e| matches!(e, Effect::Mica | Effect::Acrylic | Effect::Blur))
+  {
+    effect
+  } else {
+    return;
+  };
+
+  match effect {
+    Effect::Blur => apply_blur(window, color),
+    Effect::Acrylic => apply_acrylic(window, color),
+    Effect::Mica => apply_mica(window),
+    _ => unreachable!(),
+  }
+}
+
+pub fn clear_effects(window: HWND) {
+  clear_blur(window);
+  clear_acrylic(window);
+  clear_mica(window);
+}
+
+pub fn apply_blur(hwnd: HWND, color: Option<Color>) {
+  if is_windows_7() {
+    let bb = DWM_BLURBEHIND {
+      dwFlags: DWM_BB_ENABLE,
+      fEnable: true.into(),
+      hRgnBlur: HRGN::default(),
+      fTransitionOnMaximized: false.into(),
+    };
+    let _ = unsafe { DwmEnableBlurBehindWindow(hwnd, &bb) };
+  } else if is_swca_supported() {
+    unsafe { SetWindowCompositionAttribute(hwnd, ACCENT_STATE::ACCENT_ENABLE_BLURBEHIND, color) };
+  } else {
+    return;
+  }
+}
+
+fn clear_blur(hwnd: HWND) {
+  if is_windows_7() {
+    let bb = DWM_BLURBEHIND {
+      dwFlags: DWM_BB_ENABLE,
+      fEnable: false.into(),
+      hRgnBlur: HRGN::default(),
+      fTransitionOnMaximized: false.into(),
+    };
+    let _ = unsafe { DwmEnableBlurBehindWindow(hwnd, &bb) };
+  } else if is_swca_supported() {
+    unsafe { SetWindowCompositionAttribute(hwnd, ACCENT_STATE::ACCENT_DISABLED, None) };
+  } else {
+    return;
+  }
+}
+
+pub fn apply_acrylic(hwnd: HWND, color: Option<Color>) {
+  if is_backdroptype_supported() {
+    unsafe {
+      let _ = DwmSetWindowAttribute(
+        hwnd,
+        DWMWA_SYSTEMBACKDROP_TYPE,
+        &DWM_SYSTEMBACKDROP_TYPE::DWMSBT_TRANSIENTWINDOW as *const _ as _,
+        4,
+      );
+    }
+  } else if is_swca_supported() {
+    unsafe {
+      SetWindowCompositionAttribute(hwnd, ACCENT_STATE::ACCENT_ENABLE_ACRYLICBLURBEHIND, color);
+    }
+  } else {
+    return;
+  }
+}
+
+pub fn clear_acrylic(hwnd: HWND) {
+  if is_backdroptype_supported() {
+    unsafe {
+      let _ = DwmSetWindowAttribute(
+        hwnd,
+        DWMWA_SYSTEMBACKDROP_TYPE,
+        &DWM_SYSTEMBACKDROP_TYPE::DWMSBT_DISABLE as *const _ as _,
+        4,
+      );
+    }
+  } else if is_swca_supported() {
+    unsafe { SetWindowCompositionAttribute(hwnd, ACCENT_STATE::ACCENT_DISABLED, None) };
+  } else {
+    return;
+  }
+}
+
+pub fn apply_mica(hwnd: HWND) {
+  if is_backdroptype_supported() {
+    unsafe {
+      let _ = DwmSetWindowAttribute(
+        hwnd,
+        DWMWA_SYSTEMBACKDROP_TYPE,
+        &DWM_SYSTEMBACKDROP_TYPE::DWMSBT_MAINWINDOW as *const _ as _,
+        4,
+      );
+    }
+  } else if is_undocumented_mica_supported() {
+    let _ = unsafe { DwmSetWindowAttribute(hwnd, DWMWA_MICA_EFFECT, &1 as *const _ as _, 4) };
+  } else {
+    return;
+  }
+}
+
+pub fn clear_mica(hwnd: HWND) {
+  if is_backdroptype_supported() {
+    unsafe {
+      let _ = DwmSetWindowAttribute(
+        hwnd,
+        DWMWA_SYSTEMBACKDROP_TYPE,
+        &DWM_SYSTEMBACKDROP_TYPE::DWMSBT_DISABLE as *const _ as _,
+        4,
+      );
+    }
+  } else if is_undocumented_mica_supported() {
+    let _ = unsafe { DwmSetWindowAttribute(hwnd, DWMWA_MICA_EFFECT, &0 as *const _ as _, 4) };
+  } else {
+    return;
+  }
+}
+
+const DWMWA_MICA_EFFECT: DWMWINDOWATTRIBUTE = DWMWINDOWATTRIBUTE(1029i32);
+const DWMWA_SYSTEMBACKDROP_TYPE: DWMWINDOWATTRIBUTE = DWMWINDOWATTRIBUTE(38i32);
+
+#[repr(C)]
+struct ACCENT_POLICY {
+  AccentState: u32,
+  AccentFlags: u32,
+  GradientColor: u32,
+  AnimationId: u32,
+}
+
+type WINDOWCOMPOSITIONATTRIB = u32;
+
+#[repr(C)]
+struct WINDOWCOMPOSITIONATTRIBDATA {
+  Attrib: WINDOWCOMPOSITIONATTRIB,
+  pvData: *mut c_void,
+  cbData: usize,
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum ACCENT_STATE {
+  ACCENT_DISABLED = 0,
+  ACCENT_ENABLE_BLURBEHIND = 3,
+  ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
+}
+
+macro_rules! get_function {
+  ($lib:expr, $func:ident) => {
+    get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
+      .map(|f| unsafe { std::mem::transmute::<windows::Win32::Foundation::FARPROC, $func>(f) })
+  };
+}
+
+unsafe fn SetWindowCompositionAttribute(
+  hwnd: HWND,
+  accent_state: ACCENT_STATE,
+  color: Option<Color>,
+) {
+  type SetWindowCompositionAttribute =
+    unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
+
+  if let Some(set_window_composition_attribute) =
+    get_function!("user32.dll", SetWindowCompositionAttribute)
+  {
+    let mut color = color.unwrap_or_default();
+
+    let is_acrylic = accent_state == ACCENT_STATE::ACCENT_ENABLE_ACRYLICBLURBEHIND;
+    if is_acrylic && color.3 == 0 {
+      // acrylic doesn't like to have 0 alpha
+      color.3 = 1;
+    }
+
+    let mut policy = ACCENT_POLICY {
+      AccentState: accent_state as _,
+      AccentFlags: if is_acrylic { 0 } else { 2 },
+      GradientColor: (color.0 as u32)
+        | (color.1 as u32) << 8
+        | (color.2 as u32) << 16
+        | (color.3 as u32) << 24,
+      AnimationId: 0,
+    };
+
+    let mut data = WINDOWCOMPOSITIONATTRIBDATA {
+      Attrib: 0x13,
+      pvData: &mut policy as *mut _ as _,
+      cbData: std::mem::size_of_val(&policy),
+    };
+
+    set_window_composition_attribute(hwnd, &mut data as *mut _ as _);
+  }
+}
+
+#[allow(unused)]
+#[repr(C)]
+enum DWM_SYSTEMBACKDROP_TYPE {
+  DWMSBT_DISABLE = 1,         // None
+  DWMSBT_MAINWINDOW = 2,      // Mica
+  DWMSBT_TRANSIENTWINDOW = 3, // Acrylic
+  DWMSBT_TABBEDWINDOW = 4,    // Tabbed
+}
+
+fn is_swca_supported() -> bool {
+  is_at_least_build(17763)
+}
+
+fn is_undocumented_mica_supported() -> bool {
+  is_at_least_build(22000)
+}
+
+fn is_backdroptype_supported() -> bool {
+  is_at_least_build(22523)
+}
+
+fn is_at_least_build(build: u32) -> bool {
+  let v = windows_version().unwrap_or_default();
+  v.2 >= build
+}

+ 115 - 3
core/tauri/src/window.rs

@@ -7,6 +7,7 @@
 pub(crate) mod menu;
 
 pub use menu::{MenuEvent, MenuHandle};
+pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
 use url::Url;
 
 #[cfg(target_os = "macos")]
@@ -30,7 +31,7 @@ use crate::{
   },
   sealed::ManagerBase,
   sealed::RuntimeOrDispatch,
-  utils::config::{WindowConfig, WindowUrl},
+  utils::config::{WindowConfig, WindowEffectsConfig, WindowUrl},
   EventLoopMessage, Invoke, InvokeError, InvokeMessage, InvokeResolver, Manager, PageLoadPayload,
   Runtime, Theme, WindowEvent,
 };
@@ -226,6 +227,10 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
     let app_handle = manager.app_handle();
     let url = config.url.clone();
     let file_drop_enabled = config.file_drop_enabled;
+    let mut webview_attributes = WebviewAttributes::new(url);
+    if let Some(effects) = config.window_effects.clone() {
+      webview_attributes = webview_attributes.window_effects(effects);
+    }
     let mut builder = Self {
       manager: manager.manager().clone(),
       runtime,
@@ -234,7 +239,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
       window_builder: <R::Dispatcher as Dispatch<EventLoopMessage>>::WindowBuilder::with_config(
         config,
       ),
-      webview_attributes: WebviewAttributes::new(url),
+      webview_attributes,
       web_resource_request_handler: None,
       navigation_handler: None,
     };
@@ -333,7 +338,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
       &labels,
       web_resource_request_handler,
     )?;
-
+    let window_effects = pending.webview_attributes.window_effects.clone();
     let window = match &mut self.runtime {
       RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending),
       RuntimeOrDispatch::RuntimeHandle(handle) => handle.create_window(pending),
@@ -341,6 +346,9 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
     }
     .map(|window| self.manager.attach_window(self.app_handle.clone(), window))?;
 
+    if let Some(effects) = window_effects {
+      crate::vibrancy::set_window_effects(&window, Some(effects))?;
+    }
     self.manager.eval_script_all(format!(
       "window.__TAURI_METADATA__.__windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }})",
       window_labels_array = serde_json::to_string(&self.manager.labels())?,
@@ -604,6 +612,19 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
     self.webview_attributes.accept_first_mouse = accept;
     self
   }
+
+  /// Sets window effects.
+  ///
+  /// Requires the window to be transparent.
+  ///
+  /// ## 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
+  /// - **Linux**: Unsupported
+  pub fn effects(mut self, effects: WindowEffectsConfig) -> Self {
+    self.webview_attributes = self.webview_attributes.window_effects(effects);
+    self
+  }
 }
 
 /// Webview attributes.
@@ -1301,6 +1322,42 @@ impl<R: Runtime> Window<R> {
       .map_err(Into::into)
   }
 
+  /// Sets window effects, pass [`None`] to clear any effects applied if possible.
+  ///
+  /// Requires the window to be transparent.
+  ///
+  /// See [`EffectsBuilder`] for a convenient builder for [`WindowEffectsConfig`].
+  ///
+  ///
+  /// ```rust,no_run
+  /// use tauri::{Manager, window::{Color, Effect, EffectState, EffectsBuilder}};
+  /// tauri::Builder::default()
+  ///   .setup(|app| {
+  ///     let window = app.get_window("main").unwrap();
+  ///     window.set_effects(
+  ///       EffectsBuilder::new()
+  ///         .effect(Effect::Popover)
+  ///         .state(EffectState::Active)
+  ///         .radius(5.)
+  ///         .color(Color(0, 0, 0, 255))
+  ///         .build(),
+  ///     )?;
+  ///     Ok(())
+  ///   });
+  /// ```
+  ///
+  /// ## 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
+  /// - **Linux**: Unsupported
+  pub fn set_effects<E: Into<Option<WindowEffectsConfig>>>(&self, effects: E) -> crate::Result<()> {
+    let effects = effects.into();
+    let window = self.clone();
+    self.run_on_main_thread(move || {
+      let _ = crate::vibrancy::set_window_effects(&window, effects);
+    })
+  }
+
   /// Determines if this window should always be on top of other windows.
   pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
     self
@@ -1898,6 +1955,61 @@ impl<R: Runtime> Window<R> {
   }
 }
 
+/// The [`WindowEffectsConfig`] object builder
+#[derive(Default)]
+pub struct EffectsBuilder(WindowEffectsConfig);
+impl EffectsBuilder {
+  /// Create a new [`WindowEffectsConfig`] builder
+  pub fn new() -> Self {
+    Self(WindowEffectsConfig::default())
+  }
+
+  /// Adds effect to the [`WindowEffectsConfig`] `effects` field
+  pub fn effect(mut self, effect: Effect) -> Self {
+    self.0.effects.push(effect);
+    self
+  }
+
+  /// Adds effects to the [`WindowEffectsConfig`] `effects` field
+  pub fn effects<I: IntoIterator<Item = Effect>>(mut self, effects: I) -> Self {
+    self.0.effects.extend(effects);
+    self
+  }
+
+  /// Clears the [`WindowEffectsConfig`] `effects` field
+  pub fn clear_effects(mut self) -> Self {
+    self.0.effects.clear();
+    self
+  }
+
+  /// Sets `state` field for the [`WindowEffectsConfig`] **macOS Only**
+  pub fn state(mut self, state: EffectState) -> Self {
+    self.0.state = Some(state);
+    self
+  }
+  /// Sets `radius` field fo the [`WindowEffectsConfig`] **macOS Only**
+  pub fn radius(mut self, radius: f64) -> Self {
+    self.0.radius = Some(radius);
+    self
+  }
+  /// Sets `color` field fo the [`WindowEffectsConfig`] **Windows Only**
+  pub fn color(mut self, color: Color) -> Self {
+    self.0.color = Some(color);
+    self
+  }
+
+  /// Builds a [`WindowEffectsConfig`]
+  pub fn build(self) -> WindowEffectsConfig {
+    self.0
+  }
+}
+
+impl From<WindowEffectsConfig> for EffectsBuilder {
+  fn from(value: WindowEffectsConfig) -> Self {
+    Self(value)
+  }
+}
+
 pub(crate) const IPC_SCOPE_DOES_NOT_ALLOW: &str = "Not allowed by the scope";
 
 pub(crate) fn ipc_scope_not_found_error_message(label: &str, url: &str) -> String {

+ 7 - 7
examples/api/src-tauri/Cargo.lock

@@ -2967,7 +2967,7 @@ checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5"
 
 [[package]]
 name = "tauri"
-version = "2.0.0-alpha.8"
+version = "2.0.0-alpha.9"
 dependencies = [
  "anyhow",
  "bytes",
@@ -3016,7 +3016,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "2.0.0-alpha.4"
+version = "2.0.0-alpha.5"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -3035,7 +3035,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "2.0.0-alpha.4"
+version = "2.0.0-alpha.5"
 dependencies = [
  "base64 0.21.0",
  "brotli",
@@ -3059,7 +3059,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "2.0.0-alpha.4"
+version = "2.0.0-alpha.5"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -3115,7 +3115,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.13.0-alpha.4"
+version = "0.13.0-alpha.5"
 dependencies = [
  "gtk",
  "http",
@@ -3134,7 +3134,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.13.0-alpha.4"
+version = "0.13.0-alpha.5"
 dependencies = [
  "cocoa",
  "gtk",
@@ -3153,7 +3153,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-utils"
-version = "2.0.0-alpha.4"
+version = "2.0.0-alpha.5"
 dependencies = [
  "aes-gcm",
  "brotli",

文件差异内容过多而无法显示
+ 0 - 0
tooling/api/docs/js-api.json


+ 276 - 0
tooling/cli/schema.json

@@ -488,6 +488,17 @@
           "description": "Whether or not the window has shadow.\n\n## Platform-specific\n\n- **Windows:** - `false` has no effect on decorated window, shadow are always ON. - `true` will make ndecorated window have a 1px white border, and on Windows 11, it will have a rounded corners. - **Linux:** Unsupported.",
           "default": true,
           "type": "boolean"
+        },
+        "windowEffects": {
+          "description": "Window effects.\n\nRequires the window to be transparent.\n\n## Platform-specific:\n\n- **Windows**: If using decorations or shadows, you may want to try this workaround https://github.com/tauri-apps/tao/issues/72#issuecomment-975607891 - **Linux**: Unsupported",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/WindowEffectsConfig"
+            },
+            {
+              "type": "null"
+            }
+          ]
         }
       },
       "additionalProperties": false
@@ -551,6 +562,271 @@
         }
       ]
     },
+    "WindowEffectsConfig": {
+      "description": "The window effects configuration object",
+      "type": "object",
+      "required": [
+        "effects"
+      ],
+      "properties": {
+        "effects": {
+          "description": "List of Window effects to apply to the Window. Conflicting effects will apply the first one and ignore the rest.",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/WindowEffect"
+          }
+        },
+        "state": {
+          "description": "Window effect state **macOS Only**",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/WindowEffectState"
+            },
+            {
+              "type": "null"
+            }
+          ]
+        },
+        "radius": {
+          "description": "Window effect corner radius **macOS Only**",
+          "type": [
+            "number",
+            "null"
+          ],
+          "format": "double"
+        },
+        "color": {
+          "description": "Window effect color. Affects [`WindowEffects::Blur`] and [`WindowEffects::Acrylic`] only on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/Color"
+            },
+            {
+              "type": "null"
+            }
+          ]
+        }
+      },
+      "additionalProperties": false
+    },
+    "WindowEffect": {
+      "description": "Platform-specific window effects",
+      "oneOf": [
+        {
+          "description": "A default material appropriate for the view's effectiveAppearance. **macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "appearanceBased"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "light"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "dark"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "mediumLight"
+          ]
+        },
+        {
+          "description": "*macOS 10.14-**",
+          "deprecated": true,
+          "type": "string",
+          "enum": [
+            "ultraDark"
+          ]
+        },
+        {
+          "description": "*macOS 10.10+**",
+          "type": "string",
+          "enum": [
+            "titlebar"
+          ]
+        },
+        {
+          "description": "*macOS 10.10+**",
+          "type": "string",
+          "enum": [
+            "selection"
+          ]
+        },
+        {
+          "description": "*macOS 10.11+**",
+          "type": "string",
+          "enum": [
+            "menu"
+          ]
+        },
+        {
+          "description": "*macOS 10.11+**",
+          "type": "string",
+          "enum": [
+            "popover"
+          ]
+        },
+        {
+          "description": "*macOS 10.11+**",
+          "type": "string",
+          "enum": [
+            "sidebar"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "headerView"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "sheet"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "windowBackground"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "hudWindow"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "fullScreenUI"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "tooltip"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "contentBackground"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "underWindowBackground"
+          ]
+        },
+        {
+          "description": "*macOS 10.14+**",
+          "type": "string",
+          "enum": [
+            "underPageBackground"
+          ]
+        },
+        {
+          "description": "*Windows 11 Only**",
+          "type": "string",
+          "enum": [
+            "mica"
+          ]
+        },
+        {
+          "description": "**Windows 7/10/11(22H1) Only**\n\n## Notes\n\nThis effect has bad performance when resizing/dragging the window on Windows 11 build 22621.",
+          "type": "string",
+          "enum": [
+            "blur"
+          ]
+        },
+        {
+          "description": "**Windows 10/11 Only**\n\n## Notes\n\nThis effect has bad performance when resizing/dragging the window on Windows 10 v1903+ and Windows 11 build 22000.",
+          "type": "string",
+          "enum": [
+            "acrylic"
+          ]
+        }
+      ]
+    },
+    "WindowEffectState": {
+      "description": "Window effect state **macOS only**\n\n<https://developer.apple.com/documentation/appkit/nsvisualeffectview/state>",
+      "oneOf": [
+        {
+          "description": "Make window effect state follow the window's active state",
+          "type": "string",
+          "enum": [
+            "followsWindowActiveState"
+          ]
+        },
+        {
+          "description": "Make window effect state always active",
+          "type": "string",
+          "enum": [
+            "active"
+          ]
+        },
+        {
+          "description": "Make window effect state always inactive",
+          "type": "string",
+          "enum": [
+            "inactive"
+          ]
+        }
+      ]
+    },
+    "Color": {
+      "description": "a tuple struct of RGBA colors. Each value has minimum of 0 and maximum of 255.",
+      "type": "array",
+      "items": [
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        },
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        },
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        },
+        {
+          "type": "integer",
+          "format": "uint8",
+          "minimum": 0.0
+        }
+      ],
+      "maxItems": 4,
+      "minItems": 4
+    },
     "BundleConfig": {
       "description": "Configuration for tauri-bundler.\n\nSee more: https://tauri.app/v1/api/config#bundleconfig",
       "type": "object",

部分文件因为文件数量过多而无法显示