mirror of
https://github.com/QL-Win/QuickLook.git
synced 2026-05-07 02:00:21 +08:00
Add support for the WindowBackdrop option
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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?
|
||||
|
||||
Reference in New Issue
Block a user