This commit is contained in:
Jack Ye 2026-03-09 11:05:41 +08:00 committed by GitHub
commit e2015c4b4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,12 +11,12 @@ using System.Windows.Markup;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Effects; using System.Windows.Media.Effects;
using System.Windows.Shell; using System.Windows.Shell;
using System.Windows.Threading;
using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin; using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.Plugin.SharedModels;
using Microsoft.Win32; using Microsoft.Win32;
using System.Windows.Threading;
namespace Flow.Launcher.Core.Resource namespace Flow.Launcher.Core.Resource
{ {
@ -100,12 +100,6 @@ namespace Flow.Launcher.Core.Resource
private void UpdateResourceDictionary(ResourceDictionary dictionaryToUpdate) private void UpdateResourceDictionary(ResourceDictionary dictionaryToUpdate)
{ {
// Add new resources
if (!Application.Current.Resources.MergedDictionaries.Contains(dictionaryToUpdate))
{
Application.Current.Resources.MergedDictionaries.Add(dictionaryToUpdate);
}
// Remove old resources // Remove old resources
if (_oldResource != null && _oldResource != dictionaryToUpdate && if (_oldResource != null && _oldResource != dictionaryToUpdate &&
Application.Current.Resources.MergedDictionaries.Contains(_oldResource)) Application.Current.Resources.MergedDictionaries.Contains(_oldResource))
@ -113,7 +107,20 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources.MergedDictionaries.Remove(_oldResource); Application.Current.Resources.MergedDictionaries.Remove(_oldResource);
} }
_oldResource = dictionaryToUpdate; // Add new resources
try
{
if (!Application.Current.Resources.MergedDictionaries.Contains(dictionaryToUpdate))
{
Application.Current.Resources.MergedDictionaries.Add(dictionaryToUpdate);
}
_oldResource = dictionaryToUpdate;
}
catch (InvalidCastException)
{
// System.InvalidCastException: Unable to cast object of type 'System.Windows.Media.Color' to type 'System.Windows.Expression'.
_oldResource = null;
}
} }
/// <summary> /// <summary>
@ -124,8 +131,8 @@ namespace Flow.Launcher.Core.Resource
try try
{ {
// Load a ResourceDictionary for the specified theme. // Load a ResourceDictionary for the specified theme.
var themeName = _settings.Theme; var theme = _settings.Theme;
var dict = GetThemeResourceDictionary(themeName); var dict = GetThemeResourceDictionary(theme);
// Apply font settings to the theme resource. // Apply font settings to the theme resource.
ApplyFontSettings(dict); ApplyFontSettings(dict);
@ -221,7 +228,13 @@ namespace Flow.Launcher.Core.Resource
var foregroundPropertyValue = style.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground") var foregroundPropertyValue = style.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground")
.Select(x => x.Value).FirstOrDefault(); .Select(x => x.Value).FirstOrDefault();
if (!caretBrushPropertyValue && foregroundPropertyValue != null) if (!caretBrushPropertyValue && foregroundPropertyValue != null)
style.Setters.Add(new Setter(TextBoxBase.CaretBrushProperty, foregroundPropertyValue)); {
var newCaretValue = GetNewCaretValue(foregroundPropertyValue);
if (newCaretValue != null)
{
style.Setters.Add(new Setter(TextBoxBase.CaretBrushProperty, newCaretValue));
}
}
} }
else else
{ {
@ -246,6 +259,37 @@ namespace Flow.Launcher.Core.Resource
} }
} }
private static object GetNewCaretValue(object foregroundPropertyValue)
{
object newCaretValue;
if (foregroundPropertyValue is DynamicResourceExtension dynamicResource)
{
newCaretValue = new DynamicResourceExtension(dynamicResource.ResourceKey);
}
else if (foregroundPropertyValue is SolidColorBrush solidBrush)
{
// Create a new brush to avoid sharing mutable freezables with potential expressions
if (solidBrush.IsFrozen)
{
newCaretValue = solidBrush;
}
else
{
var newBrush = new SolidColorBrush(solidBrush.Color)
{
Opacity = solidBrush.Opacity
};
if (newBrush.CanFreeze) newBrush.Freeze();
newCaretValue = newBrush;
}
}
else
{
newCaretValue = foregroundPropertyValue;
}
return newCaretValue;
}
private ResourceDictionary GetThemeResourceDictionary(string theme) private ResourceDictionary GetThemeResourceDictionary(string theme)
{ {
var uri = GetThemePath(theme); var uri = GetThemePath(theme);
@ -275,10 +319,15 @@ namespace Flow.Launcher.Core.Resource
queryBoxStyle.Setters.Add(new Setter(Control.FontStretchProperty, fontStretch)); queryBoxStyle.Setters.Add(new Setter(Control.FontStretchProperty, fontStretch));
var caretBrushPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Any(x => x.Property.Name == "CaretBrush"); var caretBrushPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Any(x => x.Property.Name == "CaretBrush");
var foregroundPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground") var foregroundPropertyValue = queryBoxStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Foreground")?.Value;
.Select(x => x.Value).FirstOrDefault();
if (!caretBrushPropertyValue && foregroundPropertyValue != null) //otherwise BaseQueryBoxStyle will handle styling if (!caretBrushPropertyValue && foregroundPropertyValue != null) //otherwise BaseQueryBoxStyle will handle styling
queryBoxStyle.Setters.Add(new Setter(TextBoxBase.CaretBrushProperty, foregroundPropertyValue)); {
var newCaretValue = GetNewCaretValue(foregroundPropertyValue);
if (newCaretValue != null)
{
queryBoxStyle.Setters.Add(new Setter(TextBoxBase.CaretBrushProperty, newCaretValue));
}
}
// Query suggestion box's font style is aligned with query box // Query suggestion box's font style is aligned with query box
querySuggestionBoxStyle.Setters.Add(new Setter(Control.FontFamilyProperty, fontFamily)); querySuggestionBoxStyle.Setters.Add(new Setter(Control.FontFamilyProperty, fontFamily));
@ -419,20 +468,17 @@ namespace Flow.Launcher.Core.Resource
if (string.IsNullOrEmpty(path)) if (string.IsNullOrEmpty(path))
throw new DirectoryNotFoundException($"Theme path can't be found <{path}>"); throw new DirectoryNotFoundException($"Theme path can't be found <{path}>");
// Retrieve theme resource always use the resource with font settings applied.
var resourceDict = GetResourceDictionary(theme);
UpdateResourceDictionary(resourceDict);
_settings.Theme = theme; _settings.Theme = theme;
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme // Always allow re-loading default theme, in case of failure of switching to a new theme from default theme
if (_oldTheme != theme || theme == Constant.DefaultTheme) if (_oldTheme != theme || theme == Constant.DefaultTheme)
{ {
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath); _oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
} }
BlurEnabled = IsBlurTheme(); // Check if blur is enabled
var dict = GetThemeResourceDictionary(theme);
BlurEnabled = Win32Helper.IsBackdropSupported() && IsThemeBlurEnabled(dict);
// Apply blur and drop shadow effect so that we do not need to call it again // Apply blur and drop shadow effect so that we do not need to call it again
_ = RefreshFrameAsync(); _ = RefreshFrameAsync();
@ -477,11 +523,58 @@ namespace Flow.Launcher.Core.Resource
public void AddDropShadowEffectToCurrentTheme() public void AddDropShadowEffectToCurrentTheme()
{ {
var dict = GetCurrentResourceDictionary(); // Get current theme's WindowBorderStyle
var theme = _settings.Theme;
var dict = GetThemeResourceDictionary(theme);
var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null;
if (windowBorderStyle == null) return;
var windowBorderStyle = dict["WindowBorderStyle"] as Style; // Get a new unsealed style based on the old one, and copy Resources and Triggers
var newWindowBorderStyle = new Style(typeof(Border));
CopyStyle(windowBorderStyle, newWindowBorderStyle);
var effectSetter = new Setter // Identify existing Margin to calculate new Margin, and copy other setters
Setter existingMarginSetter = null;
foreach (var setterBase in windowBorderStyle.Setters)
{
if (setterBase is Setter setter)
{
// Skip existing Margin (we'll replace it)
if (setter.Property == FrameworkElement.MarginProperty)
{
existingMarginSetter = setter;
continue;
}
// Skip existing Effect (we'll ensure strictly one is added)
if (setter.Property == UIElement.EffectProperty) continue;
}
// Add other setters (e.g. Background, BorderThickness)
newWindowBorderStyle.Setters.Add(setterBase);
}
// Calculate new Margin
Thickness newMargin;
if (existingMarginSetter == null)
{
newMargin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
}
else
{
var baseMargin = (Thickness)existingMarginSetter.Value;
newMargin = new Thickness(
baseMargin.Left + ShadowExtraMargin,
baseMargin.Top + ShadowExtraMargin,
baseMargin.Right + ShadowExtraMargin,
baseMargin.Bottom + ShadowExtraMargin);
}
// Add new Margin Setter
newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, newMargin));
// Add Drop Shadow Effect Setter
newWindowBorderStyle.Setters.Add(new Setter
{ {
Property = UIElement.EffectProperty, Property = UIElement.EffectProperty,
Value = new DropShadowEffect Value = new DropShadowEffect
@ -491,62 +584,52 @@ namespace Flow.Launcher.Core.Resource
Direction = 270, Direction = 270,
BlurRadius = 30 BlurRadius = 30
} }
}; });
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is not Setter marginSetter) SetResizeBoarderThickness(newMargin);
{
var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
marginSetter = new Setter()
{
Property = FrameworkElement.MarginProperty,
Value = margin,
};
windowBorderStyle.Setters.Add(marginSetter);
SetResizeBoarderThickness(margin); Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle;
}
else
{
var baseMargin = (Thickness)marginSetter.Value;
var newMargin = new Thickness(
baseMargin.Left + ShadowExtraMargin,
baseMargin.Top + ShadowExtraMargin,
baseMargin.Right + ShadowExtraMargin,
baseMargin.Bottom + ShadowExtraMargin);
marginSetter.Value = newMargin;
SetResizeBoarderThickness(newMargin);
}
windowBorderStyle.Setters.Add(effectSetter);
UpdateResourceDictionary(dict);
} }
public void RemoveDropShadowEffectFromCurrentTheme() public void RemoveDropShadowEffectFromCurrentTheme()
{ {
var dict = GetCurrentResourceDictionary(); // Get current theme's WindowBorderStyle
var windowBorderStyle = dict["WindowBorderStyle"] as Style; var theme = _settings.Theme;
var dict = GetThemeResourceDictionary(theme);
var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null;
if (windowBorderStyle == null) return;
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == UIElement.EffectProperty) is Setter effectSetter) // Get a new unsealed style based on the old one, and copy Resources and Triggers
{ var newWindowBorderStyle = new Style(typeof(Border));
windowBorderStyle.Setters.Remove(effectSetter); CopyStyle(windowBorderStyle, newWindowBorderStyle);
}
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is Setter marginSetter) // Copy Setters, excluding the Effect setter and updating the Margin setter
foreach (var setterBase in windowBorderStyle.Setters)
{ {
var currentMargin = (Thickness)marginSetter.Value; if (setterBase is Setter setter)
var newMargin = new Thickness( {
currentMargin.Left - ShadowExtraMargin, // Skip existing Effect (We'll remove it)
currentMargin.Top - ShadowExtraMargin, if (setter.Property == UIElement.EffectProperty) continue;
currentMargin.Right - ShadowExtraMargin,
currentMargin.Bottom - ShadowExtraMargin); // Update Margin by subtracting the extra margin we added for the shadow
marginSetter.Value = newMargin; if (setter.Property == FrameworkElement.MarginProperty)
{
var currentMargin = (Thickness)setter.Value;
var newMargin = new Thickness(
currentMargin.Left - ShadowExtraMargin,
currentMargin.Top - ShadowExtraMargin,
currentMargin.Right - ShadowExtraMargin,
currentMargin.Bottom - ShadowExtraMargin);
newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, newMargin));
continue;
}
}
newWindowBorderStyle.Setters.Add(setterBase);
} }
SetResizeBoarderThickness(null); SetResizeBoarderThickness(null);
UpdateResourceDictionary(dict); Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle;
} }
public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize) public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize)
@ -596,26 +679,33 @@ namespace Flow.Launcher.Core.Resource
/// </summary> /// </summary>
public async Task RefreshFrameAsync() public async Task RefreshFrameAsync()
{ {
await Application.Current.Dispatcher.InvokeAsync(() => if (Application.Current == null) return;
if (!Application.Current.Dispatcher.CheckAccess())
{ {
// Get the actual backdrop type and drop shadow effect settings await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync, DispatcherPriority.Render);
var (backdropType, useDropShadowEffect) = GetActualValue(); return;
}
// Remove OS minimizing/maximizing animation // Get the actual backdrop type and drop shadow effect settings
// Methods.SetWindowAttribute(new WindowInteropHelper(mainWindow).Handle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 3); var (backdropType, useDropShadowEffect) = GetActualValue();
// The timing of adding the shadow effect should vary depending on whether the theme is transparent. // Remove OS minimizing/maximizing animation
if (BlurEnabled) // Methods.SetWindowAttribute(new WindowInteropHelper(mainWindow).Handle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 3);
{
AutoDropShadow(useDropShadowEffect);
}
SetBlurForWindow(_settings.Theme, backdropType);
if (!BlurEnabled) // The timing of adding the shadow effect should vary depending on whether the theme is transparent.
{ if (BlurEnabled)
AutoDropShadow(useDropShadowEffect); {
} AutoDropShadow(useDropShadowEffect);
}, DispatcherPriority.Render); }
#pragma warning disable VSTHRD103 // Call async methods when in an async method
SetBlurForWindow(_settings.Theme, backdropType);
#pragma warning restore VSTHRD103 // Call async methods when in an async method
if (!BlurEnabled)
{
AutoDropShadow(useDropShadowEffect);
}
} }
/// <summary> /// <summary>
@ -623,13 +713,17 @@ namespace Flow.Launcher.Core.Resource
/// </summary> /// </summary>
public async Task SetBlurForWindowAsync() public async Task SetBlurForWindowAsync()
{ {
await Application.Current.Dispatcher.InvokeAsync(() => if (Application.Current == null) return;
if (!Application.Current.Dispatcher.CheckAccess())
{ {
// Get the actual backdrop type and drop shadow effect settings await Application.Current?.Dispatcher.InvokeAsync(SetBlurForWindowAsync, DispatcherPriority.Render);
var (backdropType, _) = GetActualValue(); return;
}
SetBlurForWindow(_settings.Theme, backdropType); // Get the actual backdrop type and drop shadow effect settings
}, DispatcherPriority.Render); var (backdropType, _) = GetActualValue();
SetBlurForWindow(_settings.Theme, backdropType);
} }
/// <summary> /// <summary>
@ -667,21 +761,25 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return; if (mainWindow == null) return;
// Check if the theme supports blur // Check if the theme supports blur
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b; var hasBlur = IsThemeBlurEnabled(dict);
if (BlurEnabled && hasBlur && Win32Helper.IsBackdropSupported()) if (BlurEnabled && hasBlur && Win32Helper.IsBackdropSupported())
{ {
// If the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent // If the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt) if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
{ {
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background")); windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)))); var brush = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
brush.Freeze();
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, brush));
} }
else if (backdropType == BackdropTypes.Acrylic) else if (backdropType == BackdropTypes.Acrylic)
{ {
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background")); windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Colors.Transparent))); var brush = new SolidColorBrush(Colors.Transparent);
brush.Freeze();
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, brush));
} }
// For themes with blur enabled, the window border is rendered by the system, so it's treated as a simple rectangle regardless of thickness. // For themes with blur enabled, the window border is rendered by the system, so it's treated as a simple rectangle regardless of thickness.
//(This is to avoid issues when the window is forcibly changed to a rectangular shape during snap scenarios.) //(This is to avoid issues when the window is forcibly changed to a rectangular shape during snap scenarios.)
var cornerRadiusSetter = windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Border.CornerRadiusProperty); var cornerRadiusSetter = windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Border.CornerRadiusProperty);
@ -689,7 +787,7 @@ namespace Flow.Launcher.Core.Resource
cornerRadiusSetter.Value = new CornerRadius(0); cornerRadiusSetter.Value = new CornerRadius(0);
else else
windowBorderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(0))); windowBorderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(0)));
// Apply the blur effect // Apply the blur effect
Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType); Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType);
ColorizeWindow(theme, backdropType); ColorizeWindow(theme, backdropType);
@ -706,13 +804,12 @@ namespace Flow.Launcher.Core.Resource
private void AutoDropShadow(bool useDropShadowEffect) private void AutoDropShadow(bool useDropShadowEffect)
{ {
SetWindowCornerPreference("Default");
RemoveDropShadowEffectFromCurrentTheme();
if (useDropShadowEffect) if (useDropShadowEffect)
{ {
if (BlurEnabled && Win32Helper.IsBackdropSupported()) if (BlurEnabled && Win32Helper.IsBackdropSupported())
{ {
SetWindowCornerPreference("Round"); SetWindowCornerPreference("Round");
RemoveDropShadowEffectFromCurrentTheme();
} }
else else
{ {
@ -725,9 +822,11 @@ namespace Flow.Launcher.Core.Resource
if (BlurEnabled && Win32Helper.IsBackdropSupported()) if (BlurEnabled && Win32Helper.IsBackdropSupported())
{ {
SetWindowCornerPreference("Default"); SetWindowCornerPreference("Default");
RemoveDropShadowEffectFromCurrentTheme();
} }
else else
{ {
SetWindowCornerPreference("Default");
RemoveDropShadowEffectFromCurrentTheme(); RemoveDropShadowEffectFromCurrentTheme();
} }
} }
@ -746,8 +845,9 @@ namespace Flow.Launcher.Core.Resource
// for theme has not "LightBG" or "DarkBG" case. // for theme has not "LightBG" or "DarkBG" case.
private Color GetWindowBorderStyleBackground(string theme) private Color GetWindowBorderStyleBackground(string theme)
{ {
var Resources = GetThemeResourceDictionary(theme); var dict = GetThemeResourceDictionary(theme);
var windowBorderStyle = (Style)Resources["WindowBorderStyle"]; var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null;
if (windowBorderStyle == null) return Colors.Transparent; // Default is transparent
var backgroundSetter = windowBorderStyle.Setters var backgroundSetter = windowBorderStyle.Setters
.OfType<Setter>() .OfType<Setter>()
@ -765,12 +865,12 @@ namespace Flow.Launcher.Core.Resource
else if (backgroundValue is DynamicResourceExtension dynamicResource) else if (backgroundValue is DynamicResourceExtension dynamicResource)
{ {
// When DynamicResource Extension it is, Key is resource's name. // When DynamicResource Extension it is, Key is resource's name.
var resourceKey = backgroundSetter.Value.ToString(); var resourceKey = dynamicResource.ResourceKey.ToString();
// find key in resource and return color. // find key in resource and return color.
if (Resources.Contains(resourceKey)) if (dict.Contains(resourceKey))
{ {
var colorResource = Resources[resourceKey]; var colorResource = dict[resourceKey];
if (colorResource is SolidColorBrush colorBrush) if (colorResource is SolidColorBrush colorBrush)
{ {
return colorBrush.Color; return colorBrush.Color;
@ -803,7 +903,9 @@ namespace Flow.Launcher.Core.Resource
// Apply background color (remove transparency in color) // Apply background color (remove transparency in color)
Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B); Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B);
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(backgroundColor))); var brush = new SolidColorBrush(backgroundColor);
brush.Freeze();
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, brush));
// The blur theme keeps the corner round fixed (applying DWM code to modify it causes rendering issues). // The blur theme keeps the corner round fixed (applying DWM code to modify it causes rendering issues).
// The non-blur theme retains the previously set WindowBorderStyle. // The non-blur theme retains the previously set WindowBorderStyle.
@ -817,7 +919,7 @@ namespace Flow.Launcher.Core.Resource
Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle; Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle;
} }
private void CopyStyle(Style originalStyle, Style targetStyle) private static void CopyStyle(Style originalStyle, Style targetStyle)
{ {
// If the style is based on another style, copy the base style first // If the style is based on another style, copy the base style first
if (originalStyle.BasedOn != null) if (originalStyle.BasedOn != null)
@ -841,20 +943,16 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return; if (mainWindow == null) return;
// Check if the theme supports blur // Check if the theme supports blur
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b; var hasBlur = IsThemeBlurEnabled(dict);
// SystemBG value check (Auto, Light, Dark) // SystemBG value check (Auto, Light, Dark)
string systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto var systemBG = dict.Contains("SystemBG") ? dict["SystemBG"] as string : "Auto"; // 기본값 Auto
// Check the user's ColorScheme setting // Check the user's ColorScheme setting
string colorScheme = _settings.ColorScheme; var colorScheme = _settings.ColorScheme;
// Check system dark mode setting (read AppsUseLightTheme value)
int themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
bool isSystemDark = themeValue == 0;
// Final decision on whether to use dark mode // Final decision on whether to use dark mode
bool useDarkMode = false; var useDarkMode = false;
// If systemBG is not "Auto", prioritize it over ColorScheme and set the mode based on systemBG value // If systemBG is not "Auto", prioritize it over ColorScheme and set the mode based on systemBG value
if (systemBG == "Dark") if (systemBG == "Dark")
@ -869,11 +967,20 @@ namespace Flow.Launcher.Core.Resource
{ {
// If systemBG is "Auto", decide based on ColorScheme // If systemBG is "Auto", decide based on ColorScheme
if (colorScheme == "Dark") if (colorScheme == "Dark")
{
useDarkMode = true; useDarkMode = true;
}
else if (colorScheme == "Light") else if (colorScheme == "Light")
{
useDarkMode = false; useDarkMode = false;
}
else else
{
// Check system dark mode setting (read AppsUseLightTheme value)
var themeValue = (int)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
var isSystemDark = themeValue == 0;
useDarkMode = isSystemDark; // Auto (based on system setting) useDarkMode = isSystemDark; // Auto (based on system setting)
}
} }
// Apply DWM Dark Mode // Apply DWM Dark Mode
@ -906,8 +1013,8 @@ namespace Flow.Launcher.Core.Resource
Color selectedBG = useDarkMode ? DarkBG : LightBG; Color selectedBG = useDarkMode ? DarkBG : LightBG;
ApplyPreviewBackground(selectedBG); ApplyPreviewBackground(selectedBG);
bool isBlurAvailable = hasBlur && Win32Helper.IsBackdropSupported(); // Windows 11 미만이면 hasBlur를 강제 false // If Windows does not support backdrop, treat as blur unavailable
var isBlurAvailable = hasBlur && Win32Helper.IsBackdropSupported();
if (!isBlurAvailable) if (!isBlurAvailable)
{ {
mainWindow.Background = Brushes.Transparent; mainWindow.Background = Brushes.Transparent;
@ -917,23 +1024,22 @@ namespace Flow.Launcher.Core.Resource
// Only set the background to transparent if the theme supports blur // Only set the background to transparent if the theme supports blur
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt) if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
{ {
mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)); var backgroundBrush = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
backgroundBrush.Freeze();
mainWindow.Background = backgroundBrush;
} }
else else
{ {
mainWindow.Background = new SolidColorBrush(selectedBG); var backgroundBrush = new SolidColorBrush(selectedBG);
backgroundBrush.Freeze();
mainWindow.Background = backgroundBrush;
} }
} }
} }
private static bool IsBlurTheme() private static bool IsThemeBlurEnabled(ResourceDictionary dict)
{ {
if (!Win32Helper.IsBackdropSupported()) // Windows 11 미만이면 무조건 false return dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool enabled && enabled;
return false;
var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
return resource is bool b && b;
} }
#endregion #endregion