Add support for the WindowBackdrop option

This commit is contained in:
ema
2026-04-03 00:31:19 +08:00
parent 75bd49a233
commit 0647b22906
5 changed files with 326 additions and 38 deletions
+109 -8
View File
@@ -168,7 +168,49 @@ public static class WindowHelper
Marshal.FreeHGlobal(accentPtr);
}
public static void DisableBlur(Window window)
public static void EnableAcrylicBlur(Window window, Color tintColor, bool isDarkTheme)
{
window.Background = Brushes.Transparent;
var hwnd = new WindowInteropHelper(window).EnsureHandle();
if (!window.AllowsTransparency && HwndSource.FromHwnd(hwnd) is HwndSource hwndSource)
{
hwndSource.CompositionTarget.BackgroundColor = Colors.Transparent;
}
if (Environment.OSVersion.Version >= new Version(10, 0, 22000))
{
var captionColor = -2;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.CaptionColor, ref captionColor, Marshal.SizeOf(typeof(int)));
}
SetImmersiveDarkMode(hwnd, isDarkTheme);
var margins = new Dwmapi.Margins(-1, -1, -1, -1);
Dwmapi.DwmExtendFrameIntoClientArea(hwnd, ref margins);
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
accent.AccentState = AccentState.AccentEnableAcrylicblurbehind;
accent.GradientColor = ToAbgr(tintColor, 0.8);
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var data = new WindowCompositionAttributeData
{
Attribute = WindowCompositionAttribute.WcaAccentPolicy,
SizeOfData = accentStructSize,
Data = accentPtr
};
User32.SetWindowCompositionAttribute(hwnd, ref data);
Marshal.FreeHGlobal(accentPtr);
}
public static void DisableDwmBlur(Window window)
{
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
@@ -192,16 +234,45 @@ public static class WindowHelper
var margins = new Dwmapi.Margins(0, 0, 0, 0);
Dwmapi.DwmExtendFrameIntoClientArea(hwnd, ref margins);
if (Environment.OSVersion.Version >= new Version(10, 0, 21996))
{
var micaEnabled = 0;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.MicaEffect, ref micaEnabled, Marshal.SizeOf(typeof(int)));
}
if (Environment.OSVersion.Version >= new Version(10, 0, 22523))
{
var backdropType = (int)Dwmapi.SystembackdropType.None;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.SystembackdropType, ref backdropType, Marshal.SizeOf(typeof(int)));
}
else if (Environment.OSVersion.Version >= new Version(10, 0, 21996))
if (Environment.OSVersion.Version >= new Version(10, 0, 22000))
{
var micaEnabled = 0;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.MicaEffect, ref micaEnabled, Marshal.SizeOf(typeof(int)));
var captionColor = -1;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.CaptionColor, ref captionColor, Marshal.SizeOf(typeof(int)));
}
SetImmersiveDarkMode(hwnd, false);
// Restore system rounded corners
if (Environment.OSVersion.Version >= new Version(10, 0, 22000))
{
int cornerPreference = (int)Dwmapi.WindowCornerStyle.Round;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.WindowCornerPreference, ref cornerPreference, Marshal.SizeOf(typeof(int)));
}
}
private static void SetImmersiveDarkMode(nint hwnd, bool enabled)
{
if (hwnd == IntPtr.Zero || Environment.OSVersion.Version < new Version(10, 0, 17763))
return;
var darkMode = enabled ? 1 : 0;
var attribute = Environment.OSVersion.Version < new Version(10, 0, 18985)
? (uint)Dwmapi.WindowAttribute.UseImmersiveDarkModeOld
: (uint)Dwmapi.WindowAttribute.UseImmersiveDarkMode;
Dwmapi.DwmSetWindowAttribute(hwnd, attribute, ref darkMode, Marshal.SizeOf(typeof(int)));
}
private static void EnableDwmBlur(Window window, bool isDarkTheme, uint dwAttribute, int pvAttribute)
@@ -209,10 +280,21 @@ public static class WindowHelper
// Mica will handle the color
window.Background = Brushes.Transparent;
var hwnd = new WindowInteropHelper(window).Handle;
var hwnd = new WindowInteropHelper(window).EnsureHandle();
if (!window.AllowsTransparency && HwndSource.FromHwnd(hwnd) is HwndSource hwndSource)
{
hwndSource.CompositionTarget.BackgroundColor = Colors.Transparent;
}
if (Environment.OSVersion.Version >= new Version(10, 0, 22000))
{
var captionColor = -2;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.CaptionColor, ref captionColor, Marshal.SizeOf(typeof(int)));
}
var isDarkThemeInt = isDarkTheme ? 1 : 0;
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.UseImmersiveDarkMode, ref isDarkThemeInt, Marshal.SizeOf(typeof(bool)));
Dwmapi.DwmSetWindowAttribute(hwnd, (uint)Dwmapi.WindowAttribute.UseImmersiveDarkMode, ref isDarkThemeInt, Marshal.SizeOf(typeof(int)));
var margins = new Dwmapi.Margins(-1, -1, -1, -1);
Dwmapi.DwmExtendFrameIntoClientArea(hwnd, ref margins);
@@ -228,7 +310,17 @@ public static class WindowHelper
public static void EnableBackdropMicaBlur(Window window, bool isDarkTheme)
{
EnableDwmBlur(window, isDarkTheme, (uint)Dwmapi.WindowAttribute.SystembackdropType, (int)Dwmapi.SystembackdropType.MainWindow);
EnableDwmBlur(window, isDarkTheme, (uint)Dwmapi.WindowAttribute.SystembackdropType, (int)Dwmapi.SystembackdropType.Mica);
}
public static void EnableBackdropAcrylicBlur(Window window, bool isDarkTheme)
{
EnableDwmBlur(window, isDarkTheme, (uint)Dwmapi.WindowAttribute.SystembackdropType, (int)Dwmapi.SystembackdropType.Acrylic);
}
public static void EnableBackdropTabbedBlur(Window window, bool isDarkTheme)
{
EnableDwmBlur(window, isDarkTheme, (uint)Dwmapi.WindowAttribute.SystembackdropType, (int)Dwmapi.SystembackdropType.Tabbed);
}
[StructLayout(LayoutKind.Sequential)]
@@ -245,7 +337,16 @@ public static class WindowHelper
AccentEnableGradient = 1,
AccentEnableTransparentgradient = 2,
AccentEnableBlurbehind = 3,
AccentInvalidState = 4,
AccentEnableAcrylicblurbehind = 4,
AccentInvalidState = 5,
}
private static uint ToAbgr(Color color, double alphaScale)
{
return (uint)(color.R << 0 |
color.G << 8 |
color.B << 16 |
(int)(color.A * alphaScale) << 24);
}
[StructLayout(LayoutKind.Sequential)]
+35 -3
View File
@@ -32,7 +32,10 @@ public static class Dwmapi
public enum WindowAttribute
{
UseImmersiveDarkModeOld = 19,
UseImmersiveDarkMode = 20,
WindowCornerPreference = 33,
CaptionColor = 35,
SystembackdropType = 38,
MicaEffect = 1029,
}
@@ -41,9 +44,38 @@ public static class Dwmapi
{
Auto = 0,
None = 1,
MainWindow = 2,
TransientWindow = 3,
TabbedWindow = 4,
Mica = 2,
Acrylic = 3, // Automatically selects the best Acrylic effect available on the system (Acrylic11 > Acrylic10)
Tabbed = 4,
Acrylic10 = 5, // Windows 10 style, supported on Windows 10 and 11
Acrylic11 = 6, // Windows 11 style, supported on Windows 11 22523+ (Insider) and 22621+ (Stable)
}
public enum WindowCornerStyle : uint
{
/// <summary>
/// Let the system decide whether or not to round window corners.
/// Equivalent to DWMWCP_DEFAULT
/// </summary>
Default = 0,
/// <summary>
/// Never round window corners.
/// Equivalent to DWMWCP_DONOTROUND
/// </summary>
DoNotRound = 1,
/// <summary>
/// Round the corners if appropriate.
/// Equivalent to DWMWCP_ROUND
/// </summary>
Round = 2,
/// <summary>
/// Round the corners if appropriate, with a small radius.
/// Equivalent to DWMWCP_ROUNDSMALL
/// </summary>
RoundSmall = 3,
}
[DllImport("DwmApi.dll")]
+5
View File
@@ -55,7 +55,9 @@ public partial class ViewerWindow : INotifyPropertyChanged
}
public IViewer Plugin { get; private set; }
public ContextObject ContextObject { get; private set; }
public Themes CurrentTheme { get; private set; }
public ICommand CloseCommand { get; private set; }
@@ -131,5 +133,8 @@ public partial class ViewerWindow : INotifyPropertyChanged
// Update theme for WPF-UI controls
ThemeManager.Apply(ApplicationTheme.Light);
}
if (IsLoaded)
ApplyWindowBackgroundEffects();
}
}
+1 -1
View File
@@ -39,7 +39,7 @@
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="{StaticResource MainWindowCaptionHeight}"
CornerRadius="0"
GlassFrameThickness="0"
GlassFrameThickness="1"
ResizeBorderThickness="{StaticResource MainWindowResizeThickness}"
UseAeroCaptionButtons="False" />
</WindowChrome.WindowChrome>
+176 -26
View File
@@ -22,14 +22,20 @@ using QuickLook.Helpers;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shell;
using Wpf.Ui.Violeta.Controls;
using static QuickLook.Common.NativeMethods.Dwmapi;
using Brush = System.Windows.Media.Brush;
using Color = System.Windows.Media.Color;
using FontFamily = System.Windows.Media.FontFamily;
using Size = System.Windows.Size;
using SolidColorBrush = System.Windows.Media.SolidColorBrush;
namespace QuickLook;
@@ -157,48 +163,39 @@ public partial class ViewerWindow : Window
base.Close();
}
public override void OnApplyTemplate()
protected override void OnSourceInitialized(EventArgs e)
{
base.OnApplyTemplate();
base.OnSourceInitialized(e);
WindowHelper.RemoveWindowControls(this);
ApplyWindowBackgroundEffects();
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
ApplyWindowBackgroundEffects();
}
private void ApplyWindowBackgroundEffects()
{
var useTransparency = SettingHelper.Get("UseTransparency", true)
&& SystemParameters.IsGlassEnabled
&& !App.IsGPUInBlacklist;
var windowChrome = WindowChrome.GetWindowChrome(this);
windowChrome?.GlassFrameThickness = useTransparency ? new Thickness(1) : new Thickness(0);
var backdrop = GetBackdropOption();
if (useTransparency)
{
if (App.IsWin11)
{
if (Environment.OSVersion.Version >= new Version(10, 0, 22523))
{
WindowHelper.EnableBackdropMicaBlur(this, CurrentTheme == Themes.Dark);
}
else
{
WindowHelper.EnableMicaBlur(this, CurrentTheme == Themes.Dark);
}
}
else if (App.IsWin10)
{
WindowHelper.EnableBlur(this);
}
else
{
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
Background = Brushes.Transparent;
ApplyBackdrop(backdrop);
}
else
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.DisableDwmBlur(this);
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
@@ -207,15 +204,168 @@ public partial class ViewerWindow : Window
{
try
{
Background = (Brush)new System.Windows.Media.BrushConverter().ConvertFromString(customColor);
Background = (Brush)new BrushConverter().ConvertFromString(customColor);
}
catch
{
// ignore invalid color
// Ignore invalid color
}
}
}
private void ApplyBackdrop(SystembackdropType backdrop)
{
switch (backdrop)
{
case SystembackdropType.None:
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.DisableDwmBlur(this); // Fix white flash in dark mode
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
break;
case SystembackdropType.Auto:
case SystembackdropType.Mica:
default:
if (App.IsWin11)
{
if (Environment.OSVersion.Version >= new Version(10, 0, 22523))
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableBackdropMicaBlur(this, CurrentTheme == Themes.Dark);
}
else
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableMicaBlur(this, CurrentTheme == Themes.Dark);
}
}
else if (App.IsWin10)
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableBlur(this);
}
else
{
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
break;
case SystembackdropType.Acrylic:
if (App.IsWin11 && Environment.OSVersion.Version >= new Version(10, 0, 22523))
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableBackdropAcrylicBlur(this, CurrentTheme == Themes.Dark);
}
else if (App.IsWin10)
{
var acrylicTint = GetAcrylicTintColor();
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableAcrylicBlur(this, acrylicTint, CurrentTheme == Themes.Dark);
}
else
{
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
break;
case SystembackdropType.Acrylic10:
if (App.IsWin10 || App.IsWin11)
{
var acrylicTint = GetAcrylicTintColor();
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(0d);
WindowHelper.DisableDwmBlur(this); // Restore rounded corners on Windows 11
WindowHelper.EnableAcrylicBlur(this, acrylicTint, CurrentTheme == Themes.Dark);
}
else
{
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
break;
case SystembackdropType.Acrylic11:
if (App.IsWin11 && Environment.OSVersion.Version >= new Version(10, 0, 22523))
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableBackdropAcrylicBlur(this, CurrentTheme == Themes.Dark);
}
else if (App.IsWin10 || App.IsWin11)
{
var acrylicTint = GetAcrylicTintColor();
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableAcrylicBlur(this, acrylicTint, CurrentTheme == Themes.Dark);
}
else
{
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
break;
case SystembackdropType.Tabbed:
if (App.IsWin11 && Environment.OSVersion.Version >= new Version(10, 0, 22523))
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableBackdropTabbedBlur(this, CurrentTheme == Themes.Dark);
}
else if (App.IsWin10)
{
WindowChrome.GetWindowChrome(this)?.GlassFrameThickness = new Thickness(1d);
WindowHelper.EnableBlur(this);
}
else
{
Background = (Brush)FindResource("MainWindowBackgroundNoTransparent");
}
break;
}
}
private Color GetAcrylicTintColor()
{
var customColor = SettingHelper.Get("WindowBackgroundColor", string.Empty, "QuickLook");
if (!string.IsNullOrEmpty(customColor))
{
try
{
return ((SolidColorBrush)new BrushConverter().ConvertFromString(customColor)).Color;
}
catch
{
// Ignore invalid color
}
}
return ((SolidColorBrush)FindResource("MainWindowBackground")).Color;
}
private static SystembackdropType GetBackdropOption()
{
var option = SettingHelper.Get("WindowBackdrop", nameof(SystembackdropType.Auto), "QuickLook")?.Trim();
if (string.IsNullOrEmpty(option))
return SystembackdropType.Auto;
if (string.Equals(option, nameof(SystembackdropType.Acrylic), StringComparison.OrdinalIgnoreCase))
return SystembackdropType.Acrylic;
if (string.Equals(option, nameof(SystembackdropType.Tabbed), StringComparison.OrdinalIgnoreCase))
return SystembackdropType.Tabbed;
return Enum.TryParse(option, true, out SystembackdropType parsed)
? parsed
: SystembackdropType.Auto;
}
private void SaveWindowSizeOnSizeChanged(object sender, SizeChangedEventArgs e)
{
// first shown?