mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
feat(windows): add tabbed effect (#7794)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio> Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
5
.changes/api-tabbed.md
Normal file
5
.changes/api-tabbed.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tauri-apps/api": "patch:feat"
|
||||
---
|
||||
|
||||
On Windows, add `Effect.Tabbed`,`Effect.TabbedDark` and `Effect.TabbedLight` effects.
|
||||
5
.changes/tauri-tabbed.md
Normal file
5
.changes/tauri-tabbed.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": "patch:feat"
|
||||
---
|
||||
|
||||
On Windows, add `Effect::Tabbed`,`Effect::TabbedDark` and `Effect::TabbedLight` effects.
|
||||
6
.changes/tauri-utils-tabbed.md
Normal file
6
.changes/tauri-utils-tabbed.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"tauri-utils": "patch:feat"
|
||||
---
|
||||
|
||||
On Windows, add `WindowEffect::Tabbed`,`WindowEffect::TabbedDark` and `WindowEffect::TabbedLight`
|
||||
|
||||
@@ -796,6 +796,27 @@
|
||||
"micaLight"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Tabbed effect that matches the system dark perefence **Windows 11 Only**",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tabbed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Tabbed effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tabbedDark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Tabbed effect with light mode **Windows 11 Only**",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tabbedLight"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
|
||||
@@ -842,6 +842,12 @@ pub struct BundleConfig {
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Color(pub u8, pub u8, pub u8, pub u8);
|
||||
|
||||
impl From<Color> for (u8, u8, u8, u8) {
|
||||
fn from(value: Color) -> Self {
|
||||
(value.0, value.1, value.2, value.3)
|
||||
}
|
||||
}
|
||||
|
||||
/// The window effects configuration object
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
|
||||
@@ -2197,6 +2203,9 @@ mod build {
|
||||
WindowEffect::MicaLight => quote! { #prefix::MicaLight},
|
||||
WindowEffect::Blur => quote! { #prefix::Blur},
|
||||
WindowEffect::Acrylic => quote! { #prefix::Acrylic},
|
||||
WindowEffect::Tabbed => quote! { #prefix::Tabbed },
|
||||
WindowEffect::TabbedDark => quote! { #prefix::TabbedDark },
|
||||
WindowEffect::TabbedLight => quote! { #prefix::TabbedLight },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,12 @@ mod window_effects {
|
||||
MicaDark,
|
||||
/// Mica effect with light mode **Windows 11 Only**
|
||||
MicaLight,
|
||||
/// Tabbed effect that matches the system dark perefence **Windows 11 Only**
|
||||
Tabbed,
|
||||
/// Tabbed effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**
|
||||
TabbedDark,
|
||||
/// Tabbed effect with light mode **Windows 11 Only**
|
||||
TabbedLight,
|
||||
/// **Windows 7/10/11(22H1) Only**
|
||||
///
|
||||
/// ## Notes
|
||||
|
||||
@@ -71,6 +71,7 @@ infer = { version = "0.15", optional = true }
|
||||
png = { version = "0.17", optional = true }
|
||||
ico = { version = "0.3.0", optional = true }
|
||||
http-range = { version = "0.1.4", optional = true }
|
||||
window-vibrancy = "0.4"
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
|
||||
muda = { version = "0.8", default-features = false }
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,25 +7,17 @@
|
||||
|
||||
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};
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
use window_vibrancy::{NSVisualEffectMaterial, NSVisualEffectState};
|
||||
|
||||
pub fn apply_effects(window: id, effects: WindowEffectsConfig) {
|
||||
pub fn apply_effects(window: impl HasRawWindowHandle, effects: WindowEffectsConfig) {
|
||||
let WindowEffectsConfig {
|
||||
effects,
|
||||
radius,
|
||||
state,
|
||||
..
|
||||
} = effects;
|
||||
let mut appearance: NSVisualEffectMaterial = if let Some(effect) = effects.into_iter().find(|e| {
|
||||
let effect = if let Some(effect) = effects.into_iter().find(|e| {
|
||||
matches!(
|
||||
e,
|
||||
Effect::AppearanceBased
|
||||
@@ -49,221 +41,14 @@ pub fn apply_effects(window: id, effects: WindowEffectsConfig) {
|
||||
| Effect::UnderPageBackground
|
||||
)
|
||||
}) {
|
||||
effect.into()
|
||||
effect
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
unsafe {
|
||||
if NSAppKitVersionNumber < NSAppKitVersionNumber10_10 {
|
||||
return;
|
||||
}
|
||||
|
||||
if !msg_send![class!(NSThread), isMainThread] {
|
||||
return;
|
||||
}
|
||||
|
||||
if appearance as u32 > 4 && NSAppKitVersionNumber < NSAppKitVersionNumber10_11 {
|
||||
appearance = NSVisualEffectMaterial::AppearanceBased;
|
||||
}
|
||||
|
||||
if appearance as u32 > 9 && NSAppKitVersionNumber < NSAppKitVersionNumber10_14 {
|
||||
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, Eq)]
|
||||
pub enum NSVisualEffectMaterial {
|
||||
#[deprecated = "Since macOS 10.14 a default material appropriate for the view's effectiveAppearance. You should instead choose an appropriate semantic material."]
|
||||
AppearanceBased = 0,
|
||||
#[deprecated = "Since macOS 10.14 use a semantic material instead."]
|
||||
Light = 1,
|
||||
#[deprecated = "Since macOS 10.14 use a semantic material instead."]
|
||||
Dark = 2,
|
||||
#[deprecated = "Since macOS 10.14 use a semantic material instead."]
|
||||
MediumLight = 8,
|
||||
#[deprecated = "Since macOS 10.14 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, Eq)]
|
||||
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 {
|
||||
window_vibrancy::apply_vibrancy(
|
||||
window,
|
||||
match effect {
|
||||
Effect::AppearanceBased => NSVisualEffectMaterial::AppearanceBased,
|
||||
Effect::Light => NSVisualEffectMaterial::Light,
|
||||
Effect::Dark => NSVisualEffectMaterial::Dark,
|
||||
@@ -284,16 +69,12 @@ impl From<crate::window::Effect> for NSVisualEffectMaterial {
|
||||
Effect::UnderWindowBackground => NSVisualEffectMaterial::UnderWindowBackground,
|
||||
Effect::UnderPageBackground => NSVisualEffectMaterial::UnderPageBackground,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::window::EffectState> for NSVisualEffectState {
|
||||
fn from(value: crate::window::EffectState) -> Self {
|
||||
match value {
|
||||
},
|
||||
state.map(|s| match s {
|
||||
EffectState::FollowsWindowActiveState => NSVisualEffectState::FollowsWindowActiveState,
|
||||
EffectState::Active => NSVisualEffectState::Active,
|
||||
EffectState::Inactive => NSVisualEffectState::Inactive,
|
||||
}
|
||||
}
|
||||
}),
|
||||
radius,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,21 +19,12 @@ pub fn set_window_effects<R: Runtime>(
|
||||
) -> crate::Result<()> {
|
||||
if let Some(_effects) = effects {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let hwnd = window.hwnd()?;
|
||||
windows::apply_effects(hwnd, _effects);
|
||||
}
|
||||
windows::apply_effects(window, _effects);
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let ns_window = window.ns_window()?;
|
||||
macos::apply_effects(ns_window as _, _effects);
|
||||
}
|
||||
macos::apply_effects(window, _effects);
|
||||
} else {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let hwnd = window.hwnd()?;
|
||||
windows::clear_effects(hwnd);
|
||||
}
|
||||
windows::clear_effects(window);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,24 +11,23 @@ use std::ffi::c_void;
|
||||
|
||||
use crate::utils::config::WindowEffectsConfig;
|
||||
use crate::window::{Color, Effect};
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
use tauri_utils::platform::{get_function_impl, is_windows_7, windows_version};
|
||||
use windows::Win32::Graphics::Dwm::{
|
||||
DwmSetWindowAttribute, DWMWA_USE_IMMERSIVE_DARK_MODE, DWMWINDOWATTRIBUTE,
|
||||
};
|
||||
use windows::Win32::{
|
||||
Foundation::{BOOL, HWND},
|
||||
Graphics::{
|
||||
Dwm::{DwmEnableBlurBehindWindow, DWM_BB_ENABLE, DWM_BLURBEHIND},
|
||||
Gdi::HRGN,
|
||||
},
|
||||
};
|
||||
use windows::Win32::Foundation::HWND;
|
||||
|
||||
pub fn apply_effects(window: HWND, effects: WindowEffectsConfig) {
|
||||
pub fn apply_effects(window: impl HasRawWindowHandle, effects: WindowEffectsConfig) {
|
||||
let WindowEffectsConfig { effects, color, .. } = effects;
|
||||
let effect = if let Some(effect) = effects.iter().find(|e| {
|
||||
matches!(
|
||||
e,
|
||||
Effect::Mica | Effect::MicaDark | Effect::MicaLight | Effect::Acrylic | Effect::Blur
|
||||
Effect::Mica
|
||||
| Effect::MicaDark
|
||||
| Effect::MicaLight
|
||||
| Effect::Acrylic
|
||||
| Effect::Blur
|
||||
| Effect::Tabbed
|
||||
| Effect::TabbedDark
|
||||
| Effect::TabbedLight
|
||||
)
|
||||
}) {
|
||||
effect
|
||||
@@ -37,230 +36,20 @@ pub fn apply_effects(window: HWND, effects: WindowEffectsConfig) {
|
||||
};
|
||||
|
||||
match effect {
|
||||
Effect::Blur => apply_blur(window, color),
|
||||
Effect::Acrylic => apply_acrylic(window, color),
|
||||
Effect::Mica => apply_mica(window, None),
|
||||
Effect::MicaDark => apply_mica(window, Some(true)),
|
||||
Effect::MicaLight => apply_mica(window, Some(false)),
|
||||
Effect::Blur => window_vibrancy::apply_blur(window, color.map(Into::into)),
|
||||
Effect::Acrylic => window_vibrancy::apply_acrylic(window, color.map(Into::into)),
|
||||
Effect::Mica => window_vibrancy::apply_mica(window, None),
|
||||
Effect::MicaDark => window_vibrancy::apply_mica(window, Some(true)),
|
||||
Effect::MicaLight => window_vibrancy::apply_mica(window, Some(false)),
|
||||
Effect::Tabbed => window_vibrancy::apply_tabbed(window, None),
|
||||
Effect::TabbedDark => window_vibrancy::apply_tabbed(window, Some(true)),
|
||||
Effect::TabbedLight => window_vibrancy::apply_tabbed(window, Some(false)),
|
||||
_ => 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, dark: Option<bool>) {
|
||||
if let Some(dark) = dark {
|
||||
unsafe {
|
||||
DwmSetWindowAttribute(
|
||||
hwnd,
|
||||
DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
&(dark as u32) as *const _ as _,
|
||||
4,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
pub fn clear_effects(window: impl HasRawWindowHandle) {
|
||||
window_vibrancy::clear_blur(&window);
|
||||
window_vibrancy::clear_acrylic(&window);
|
||||
window_vibrancy::clear_mica(&window);
|
||||
}
|
||||
|
||||
28
examples/api/dist/assets/index.js
vendored
28
examples/api/dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
17
examples/api/src-tauri/Cargo.lock
generated
17
examples/api/src-tauri/Cargo.lock
generated
@@ -2137,9 +2137,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "muda"
|
||||
version = "0.8.2"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e33f46fb20f85553edb85e30a6a5231567f4103276ccdb5a2050613d253b1d"
|
||||
checksum = "41fe753ec4d3e8137a1d3ecb1aee1192b8f7661fe1247641968f5bf5f2e6ebbe"
|
||||
dependencies = [
|
||||
"cocoa 0.25.0",
|
||||
"crossbeam-channel",
|
||||
@@ -3445,6 +3445,7 @@ dependencies = [
|
||||
"uuid",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
"windows",
|
||||
]
|
||||
|
||||
@@ -4295,6 +4296,18 @@ dependencies = [
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "window-vibrancy"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5931735e675b972fada30c7a402915d4d827aa5ef6c929c133d640c4b785e963"
|
||||
dependencies = [
|
||||
"cocoa 0.25.0",
|
||||
"objc",
|
||||
"raw-window-handle",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
|
||||
@@ -60,9 +60,9 @@
|
||||
"rowResize",
|
||||
];
|
||||
|
||||
const windowsEffects = ["mica", "blur", "acrylic"];
|
||||
const isWindows = navigator.appVersion.includes("windows");
|
||||
const isMacOS = navigator.appVersion.includes("macos");
|
||||
const windowsEffects = ["mica", "blur", "acrylic", "tabbed", "tabbedDark", "tabbedLight"];
|
||||
const isWindows = navigator.appVersion.includes("Windows");
|
||||
const isMacOS = navigator.appVersion.includes("Macintosh");
|
||||
let effectOptions = isWindows
|
||||
? windowsEffects
|
||||
: Object.keys(Effect)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1944,7 +1944,19 @@ enum Effect {
|
||||
*
|
||||
* This effect has bad performance when resizing/dragging the window on Windows 10 v1903+ and Windows 11 build 22000.
|
||||
*/
|
||||
Acrylic = 'acrylic'
|
||||
Acrylic = 'acrylic',
|
||||
/**
|
||||
* Tabbed effect that matches the system dark perefence **Windows 11 Only**
|
||||
*/
|
||||
Tabbed = 'tabbed',
|
||||
/**
|
||||
* Tabbed effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**
|
||||
*/
|
||||
TabbedDark = 'tabbedDark',
|
||||
/**
|
||||
* Tabbed effect with light mode **Windows 11 Only**
|
||||
*/
|
||||
TabbedLight = 'tabbedLight'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1988,7 +2000,7 @@ interface Effects {
|
||||
*/
|
||||
radius?: number
|
||||
/**
|
||||
* Window effect color. Affects {@link Effects.Blur} and {@link Effects.Acrylic} only
|
||||
* Window effect color. Affects {@link Effect.Blur} and {@link Effect.Acrylic} only
|
||||
* on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.
|
||||
*/
|
||||
color?: Color
|
||||
|
||||
@@ -796,6 +796,27 @@
|
||||
"micaLight"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Tabbed effect that matches the system dark perefence **Windows 11 Only**",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tabbed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Tabbed effect with dark mode but only if dark mode is enabled on the system **Windows 11 Only**",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tabbedDark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Tabbed effect with light mode **Windows 11 Only**",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tabbedLight"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
|
||||
Reference in New Issue
Block a user