Compare commits

...

22 commits

Author SHA1 Message Date
Jack Ye
11069b20da
Merge 7476cdd894 into 1bdc1164f8 2026-03-08 21:27:16 +11:00
Jack Ye
7476cdd894
Simplify theme refresh logic in ActualApplicationThemeChanged 2026-03-02 18:04:00 +08:00
Jack251970
65042d9fe1 Update WindowBorderStyle to use global app resources
Changed assignment of WindowBorderStyle to use Application.Current.Resources for global effect, replacing previous use of _oldResource. This ensures theme changes are applied consistently across the application.
2026-03-01 14:28:53 +08:00
Jack251970
46cecb2acc Improve code quality 2026-03-01 14:26:12 +08:00
Jack251970
e3d71f94ef Refactor window border style creation and copying
Replaces GetNewWindowBorderStyle with direct instantiation of a new Style for Border and uses CopyStyle to transfer properties. Removes the old helper method and moves style copying logic inline for improved clarity and maintainability.
2026-03-01 14:23:57 +08:00
Jack251970
4767b444ec Optimize SolidColorBrush usage for window background
Freeze SolidColorBrush instances before assigning them to mainWindow.Background to improve performance and immutability. Applied to both transparent and selected background scenarios.
2026-03-01 14:16:36 +08:00
Jack251970
428a7ad0c2 Prioritize user ColorScheme over system dark mode setting
Refactored dark mode logic to first use user's ColorScheme
preference when "SystemBG" is set to "Auto". Only falls back
to system dark mode setting if ColorScheme is not explicitly
set to "Dark" or "Light". This ensures user preferences take
precedence over system defaults.
2026-03-01 14:15:15 +08:00
Jack251970
2e6fb61248 Refactor blur theme logic and helper function
Renamed IsBlurTheme to IsThemeBlurEnabled and moved system
backdrop support checks out of the helper function. Blur
availability is now determined by combining theme settings
and system capability in the calling code. Updated all
usages to reflect the new logic for improved clarity.
2026-03-01 14:13:32 +08:00
Jack251970
c87f3d3447 Improve theme resource access robustness
Refactored theme resource dictionary lookups to safely check for "WindowBorderStyle" before accessing, preventing potential exceptions. Updated three methods to handle missing styles gracefully and standardized resource key checks. GetWindowBorderStyleBackground now returns transparent if style is missing.
2026-03-01 14:05:36 +08:00
Jack251970
3db201f4a3 Use DispatcherPriority.Render for theme UI updates
Explicitly set DispatcherPriority.Render when invoking RefreshFrameAsync and SetBlurForWindowAsync on the application's dispatcher. This ensures theme-related UI changes are processed with rendering priority, improving responsiveness and visual consistency. Also added System.Windows.Threading import for access to DispatcherPriority.
2026-03-01 14:00:07 +08:00
Jack251970
9943b72839 Refactor drop shadow handling for window border style
Reworked how drop shadow effects are added and removed by creating new unsealed Style instances instead of modifying sealed ones. Resources, triggers, and setters are copied, with margin adjustments and effect setter management to ensure reliability. Introduced GetNewWindowBorderStyle helper for style copying.
2026-03-01 13:59:32 +08:00
Jack251970
5b6b7e728e Rename themeName to theme in UpdateFonts for consistency
Renamed the variable themeName to theme in the UpdateFonts method
to improve naming consistency. No functional changes were made.
2026-03-01 13:59:15 +08:00
Jack251970
340f2cbc04 Refactor blur theme detection for accuracy
Improve IsBlurTheme to check blur support directly in the
current theme's resource dictionary, ensuring precise and
context-specific detection. Updated related logic and comments
for clarity.
2026-03-01 12:19:26 +08:00
Jack251970
84ed1fa1e1 Add null checks and fix dispatcher invocation in Theme
Added early returns if Application.Current is null in RefreshFrameAsync and SetBlurForWindowAsync to prevent null reference exceptions. Updated dispatcher access checks and ensured the correct method is invoked asynchronously for each case, improving robustness and reliability.
2026-02-28 23:22:06 +08:00
Jack251970
a83da2b00f Improve Theme resource handling and suppress async warnings
Add try-catch to UpdateResourceDictionary to prevent crashes from InvalidCastException when updating resources. Set _oldResource to null on error. Suppress VSTHRD103 warnings around SetBlurForWindow to clarify intentional async usage and avoid build warnings.
2026-02-28 23:08:53 +08:00
Jack251970
7b855745d9 Refactor drop shadow logic in Theme.cs for accuracy
Moved RemoveDropShadowEffectFromCurrentTheme() into specific conditional branches within AutoDropShadow to ensure it is only called when appropriate. Explicitly set window corner preference and drop shadow effect based on theme and blur support, improving appearance consistency and logic clarity.
2026-02-28 22:45:23 +08:00
Jack251970
966bcdd5ec Refactor UI thread checks in Theme async methods
Refactored RefreshFrameAsync and SetBlurForWindowAsync to check Dispatcher access before invoking UI updates, ensuring thread safety and reducing unnecessary Dispatcher calls. Also removed redundant code and unused using directive.
2026-02-28 22:03:55 +08:00
Jack251970
5635783973 Limit theme refresh to when color scheme is "System"
Previously, the theme was refreshed on every theme change event.
Now, `_theme.RefreshFrameAsync()` is only called if the color
scheme setting is set to "System", preventing unnecessary
refreshes in other scenarios.
2026-02-28 21:23:20 +08:00
Jack251970
60b92c5b96 Improve brush handling and resource safety in theme styles
Refactored caret and background brush assignment to avoid sharing mutable instances and ensure proper resource referencing. Added GetNewCaretValue helper for safe caret brush creation. Brushes for backgrounds are now frozen for performance. Improved foreground value retrieval and dynamic resource key extraction. Made some methods static for clarity and consistency. Enhances resource management and reliability in theme handling.
2026-02-28 15:14:34 +08:00
Jack251970
41d5e0a27a Revert "Fix: Silently handle InvalidCastException from WPF system resource invalidation on Windows theme change"
This reverts commit 372f142b49.
2026-02-28 14:39:15 +08:00
copilot-swe-agent[bot]
372f142b49 Fix: Silently handle InvalidCastException from WPF system resource invalidation on Windows theme change
Co-authored-by: Jack251970 <53996452+Jack251970@users.noreply.github.com>
2026-02-27 14:45:56 +00:00
copilot-swe-agent[bot]
1146d53792 Initial plan 2026-02-27 14:39:36 +00:00

View file

@ -11,12 +11,12 @@ using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Shell;
using System.Windows.Threading;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
using Microsoft.Win32;
using System.Windows.Threading;
namespace Flow.Launcher.Core.Resource
{
@ -100,12 +100,6 @@ namespace Flow.Launcher.Core.Resource
private void UpdateResourceDictionary(ResourceDictionary dictionaryToUpdate)
{
// Add new resources
if (!Application.Current.Resources.MergedDictionaries.Contains(dictionaryToUpdate))
{
Application.Current.Resources.MergedDictionaries.Add(dictionaryToUpdate);
}
// Remove old resources
if (_oldResource != null && _oldResource != dictionaryToUpdate &&
Application.Current.Resources.MergedDictionaries.Contains(_oldResource))
@ -113,7 +107,20 @@ namespace Flow.Launcher.Core.Resource
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>
@ -124,8 +131,8 @@ namespace Flow.Launcher.Core.Resource
try
{
// Load a ResourceDictionary for the specified theme.
var themeName = _settings.Theme;
var dict = GetThemeResourceDictionary(themeName);
var theme = _settings.Theme;
var dict = GetThemeResourceDictionary(theme);
// Apply font settings to the theme resource.
ApplyFontSettings(dict);
@ -221,7 +228,13 @@ namespace Flow.Launcher.Core.Resource
var foregroundPropertyValue = style.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground")
.Select(x => x.Value).FirstOrDefault();
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
{
@ -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)
{
var uri = GetThemePath(theme);
@ -275,10 +319,15 @@ namespace Flow.Launcher.Core.Resource
queryBoxStyle.Setters.Add(new Setter(Control.FontStretchProperty, fontStretch));
var caretBrushPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Any(x => x.Property.Name == "CaretBrush");
var foregroundPropertyValue = queryBoxStyle.Setters.OfType<Setter>().Where(x => x.Property.Name == "Foreground")
.Select(x => x.Value).FirstOrDefault();
var foregroundPropertyValue = queryBoxStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Foreground")?.Value;
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
querySuggestionBoxStyle.Setters.Add(new Setter(Control.FontFamilyProperty, fontFamily));
@ -419,20 +468,17 @@ namespace Flow.Launcher.Core.Resource
if (string.IsNullOrEmpty(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;
//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)
{
_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
_ = RefreshFrameAsync();
@ -477,11 +523,58 @@ namespace Flow.Launcher.Core.Resource
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,
Value = new DropShadowEffect
@ -491,62 +584,52 @@ namespace Flow.Launcher.Core.Resource
Direction = 270,
BlurRadius = 30
}
};
});
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is not Setter marginSetter)
{
var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
marginSetter = new Setter()
{
Property = FrameworkElement.MarginProperty,
Value = margin,
};
windowBorderStyle.Setters.Add(marginSetter);
SetResizeBoarderThickness(newMargin);
SetResizeBoarderThickness(margin);
}
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);
Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle;
}
public void RemoveDropShadowEffectFromCurrentTheme()
{
var dict = GetCurrentResourceDictionary();
var windowBorderStyle = dict["WindowBorderStyle"] as Style;
// 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;
if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == UIElement.EffectProperty) is Setter effectSetter)
{
windowBorderStyle.Setters.Remove(effectSetter);
}
// Get a new unsealed style based on the old one, and copy Resources and Triggers
var newWindowBorderStyle = new Style(typeof(Border));
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;
var newMargin = new Thickness(
currentMargin.Left - ShadowExtraMargin,
currentMargin.Top - ShadowExtraMargin,
currentMargin.Right - ShadowExtraMargin,
currentMargin.Bottom - ShadowExtraMargin);
marginSetter.Value = newMargin;
if (setterBase is Setter setter)
{
// Skip existing Effect (We'll remove it)
if (setter.Property == UIElement.EffectProperty) continue;
// Update Margin by subtracting the extra margin we added for the shadow
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);
UpdateResourceDictionary(dict);
Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle;
}
public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize)
@ -596,26 +679,33 @@ namespace Flow.Launcher.Core.Resource
/// </summary>
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
var (backdropType, useDropShadowEffect) = GetActualValue();
await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync, DispatcherPriority.Render);
return;
}
// Remove OS minimizing/maximizing animation
// Methods.SetWindowAttribute(new WindowInteropHelper(mainWindow).Handle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 3);
// Get the actual backdrop type and drop shadow effect settings
var (backdropType, useDropShadowEffect) = GetActualValue();
// The timing of adding the shadow effect should vary depending on whether the theme is transparent.
if (BlurEnabled)
{
AutoDropShadow(useDropShadowEffect);
}
SetBlurForWindow(_settings.Theme, backdropType);
// Remove OS minimizing/maximizing animation
// Methods.SetWindowAttribute(new WindowInteropHelper(mainWindow).Handle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 3);
if (!BlurEnabled)
{
AutoDropShadow(useDropShadowEffect);
}
}, DispatcherPriority.Render);
// The timing of adding the shadow effect should vary depending on whether the theme is transparent.
if (BlurEnabled)
{
AutoDropShadow(useDropShadowEffect);
}
#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>
@ -623,13 +713,17 @@ namespace Flow.Launcher.Core.Resource
/// </summary>
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
var (backdropType, _) = GetActualValue();
await Application.Current?.Dispatcher.InvokeAsync(SetBlurForWindowAsync, DispatcherPriority.Render);
return;
}
SetBlurForWindow(_settings.Theme, backdropType);
}, DispatcherPriority.Render);
// Get the actual backdrop type and drop shadow effect settings
var (backdropType, _) = GetActualValue();
SetBlurForWindow(_settings.Theme, backdropType);
}
/// <summary>
@ -667,21 +761,25 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return;
// 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 the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent
if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt)
{
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)
{
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.
//(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);
@ -689,7 +787,7 @@ namespace Flow.Launcher.Core.Resource
cornerRadiusSetter.Value = new CornerRadius(0);
else
windowBorderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(0)));
// Apply the blur effect
Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType);
ColorizeWindow(theme, backdropType);
@ -706,13 +804,12 @@ namespace Flow.Launcher.Core.Resource
private void AutoDropShadow(bool useDropShadowEffect)
{
SetWindowCornerPreference("Default");
RemoveDropShadowEffectFromCurrentTheme();
if (useDropShadowEffect)
{
if (BlurEnabled && Win32Helper.IsBackdropSupported())
{
SetWindowCornerPreference("Round");
RemoveDropShadowEffectFromCurrentTheme();
}
else
{
@ -725,9 +822,11 @@ namespace Flow.Launcher.Core.Resource
if (BlurEnabled && Win32Helper.IsBackdropSupported())
{
SetWindowCornerPreference("Default");
RemoveDropShadowEffectFromCurrentTheme();
}
else
{
SetWindowCornerPreference("Default");
RemoveDropShadowEffectFromCurrentTheme();
}
}
@ -746,8 +845,9 @@ namespace Flow.Launcher.Core.Resource
// for theme has not "LightBG" or "DarkBG" case.
private Color GetWindowBorderStyleBackground(string theme)
{
var Resources = GetThemeResourceDictionary(theme);
var windowBorderStyle = (Style)Resources["WindowBorderStyle"];
var dict = GetThemeResourceDictionary(theme);
var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null;
if (windowBorderStyle == null) return Colors.Transparent; // Default is transparent
var backgroundSetter = windowBorderStyle.Setters
.OfType<Setter>()
@ -765,12 +865,12 @@ namespace Flow.Launcher.Core.Resource
else if (backgroundValue is DynamicResourceExtension dynamicResource)
{
// 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.
if (Resources.Contains(resourceKey))
if (dict.Contains(resourceKey))
{
var colorResource = Resources[resourceKey];
var colorResource = dict[resourceKey];
if (colorResource is SolidColorBrush colorBrush)
{
return colorBrush.Color;
@ -803,7 +903,9 @@ namespace Flow.Launcher.Core.Resource
// Apply background color (remove transparency in color)
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 non-blur theme retains the previously set WindowBorderStyle.
@ -817,7 +919,7 @@ namespace Flow.Launcher.Core.Resource
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 (originalStyle.BasedOn != null)
@ -841,20 +943,16 @@ namespace Flow.Launcher.Core.Resource
if (mainWindow == null) return;
// 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)
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
string 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;
var colorScheme = _settings.ColorScheme;
// 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 == "Dark")
@ -869,11 +967,20 @@ namespace Flow.Launcher.Core.Resource
{
// If systemBG is "Auto", decide based on ColorScheme
if (colorScheme == "Dark")
{
useDarkMode = true;
}
else if (colorScheme == "Light")
{
useDarkMode = false;
}
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)
}
}
// Apply DWM Dark Mode
@ -906,8 +1013,8 @@ namespace Flow.Launcher.Core.Resource
Color selectedBG = useDarkMode ? DarkBG : LightBG;
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)
{
mainWindow.Background = Brushes.Transparent;
@ -917,23 +1024,22 @@ namespace Flow.Launcher.Core.Resource
// Only set the background to transparent if the theme supports blur
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
{
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 false;
var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
return resource is bool b && b;
return dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool enabled && enabled;
}
#endregion