diff --git a/QuickLook.Common/Helpers/WindowHelper.cs b/QuickLook.Common/Helpers/WindowHelper.cs
index 981ab30..2c13af1 100644
--- a/QuickLook.Common/Helpers/WindowHelper.cs
+++ b/QuickLook.Common/Helpers/WindowHelper.cs
@@ -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)]
diff --git a/QuickLook.Common/NativeMethods/Dwmapi.cs b/QuickLook.Common/NativeMethods/Dwmapi.cs
index 99c1435..ebf7f43 100644
--- a/QuickLook.Common/NativeMethods/Dwmapi.cs
+++ b/QuickLook.Common/NativeMethods/Dwmapi.cs
@@ -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
+ {
+ ///
+ /// Let the system decide whether or not to round window corners.
+ /// Equivalent to DWMWCP_DEFAULT
+ ///
+ Default = 0,
+
+ ///
+ /// Never round window corners.
+ /// Equivalent to DWMWCP_DONOTROUND
+ ///
+ DoNotRound = 1,
+
+ ///
+ /// Round the corners if appropriate.
+ /// Equivalent to DWMWCP_ROUND
+ ///
+ Round = 2,
+
+ ///
+ /// Round the corners if appropriate, with a small radius.
+ /// Equivalent to DWMWCP_ROUNDSMALL
+ ///
+ RoundSmall = 3,
}
[DllImport("DwmApi.dll")]
diff --git a/QuickLook/ViewerWindow.Properties.cs b/QuickLook/ViewerWindow.Properties.cs
index 17ce997..6b30fa4 100644
--- a/QuickLook/ViewerWindow.Properties.cs
+++ b/QuickLook/ViewerWindow.Properties.cs
@@ -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();
}
}
diff --git a/QuickLook/ViewerWindow.xaml b/QuickLook/ViewerWindow.xaml
index 2a5f584..7ea0695 100644
--- a/QuickLook/ViewerWindow.xaml
+++ b/QuickLook/ViewerWindow.xaml
@@ -39,7 +39,7 @@
diff --git a/QuickLook/ViewerWindow.xaml.cs b/QuickLook/ViewerWindow.xaml.cs
index f14d076..cae8a8f 100644
--- a/QuickLook/ViewerWindow.xaml.cs
+++ b/QuickLook/ViewerWindow.xaml.cs
@@ -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?