From 1146d53792c0501afc1b90c6f3b3a308e307093e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:39:36 +0000 Subject: [PATCH 01/21] Initial plan From 372f142b49e00e2e5210eed3e401137c5c430a67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:45:56 +0000 Subject: [PATCH 02/21] Fix: Silently handle InvalidCastException from WPF system resource invalidation on Windows theme change Co-authored-by: Jack251970 <53996452+Jack251970@users.noreply.github.com> --- Flow.Launcher/Helper/ErrorReporting.cs | 6 ++++++ Flow.Launcher/Helper/ExceptionHelper.cs | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Flow.Launcher/Helper/ErrorReporting.cs b/Flow.Launcher/Helper/ErrorReporting.cs index 797f31482ce..e2431fd4f05 100644 --- a/Flow.Launcher/Helper/ErrorReporting.cs +++ b/Flow.Launcher/Helper/ErrorReporting.cs @@ -25,6 +25,12 @@ private static void Report(Exception e, bool silent = false, [CallerMemberName] // This change modifies the behavior to log the exception instead of showing the "Error report UI". if (ExceptionHelper.IsRecoverableDwmCompositionException(e)) return; + // Workaround for a WPF issue where changing the Windows theme or accent color triggers + // SystemResources.InvalidateTreeResources, which tries to clone Color values stored in styles + // and fails with an InvalidCastException. This is a benign framework-level exception that + // does not affect Flow Launcher functionality, so we log it silently instead of showing the error dialog. + if (ExceptionHelper.IsRecoverableSystemResourceException(e)) return; + var reportWindow = new ReportWindow(e); reportWindow.Show(); } diff --git a/Flow.Launcher/Helper/ExceptionHelper.cs b/Flow.Launcher/Helper/ExceptionHelper.cs index 5dd57f9bbcd..0cc7747ad7c 100644 --- a/Flow.Launcher/Helper/ExceptionHelper.cs +++ b/Flow.Launcher/Helper/ExceptionHelper.cs @@ -39,4 +39,23 @@ internal static bool IsRecoverableDwmCompositionException(Exception exception) return !string.IsNullOrEmpty(stackTrace) && stackTrace.Contains("DwmCompositionChanged", StringComparison.OrdinalIgnoreCase); } + + /// + /// Returns true if the exception is a recoverable WPF system resource invalidation exception + /// that occurs when Windows changes its theme or accent colors. This is a known WPF issue where + /// Color values stored in styles are incorrectly cloned during resource tree invalidation. + /// + internal static bool IsRecoverableSystemResourceException(Exception exception) + { + if (exception is not InvalidCastException) + { + return false; + } + + // Check for the specific Color-to-Expression cast failure originating from WPF's + // SystemResources.InvalidateTreeResources, triggered by Windows theme/accent color changes. + var stackTrace = exception.StackTrace; + return !string.IsNullOrEmpty(stackTrace) && + stackTrace.Contains("System.Windows.SystemResources.InvalidateTreeResources", StringComparison.Ordinal); + } } From 41d5e0a27a20eec110beca672c4217bbcbb83feb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Feb 2026 14:39:15 +0800 Subject: [PATCH 03/21] Revert "Fix: Silently handle InvalidCastException from WPF system resource invalidation on Windows theme change" This reverts commit 372f142b49e00e2e5210eed3e401137c5c430a67. --- Flow.Launcher/Helper/ErrorReporting.cs | 6 ------ Flow.Launcher/Helper/ExceptionHelper.cs | 19 ------------------- 2 files changed, 25 deletions(-) diff --git a/Flow.Launcher/Helper/ErrorReporting.cs b/Flow.Launcher/Helper/ErrorReporting.cs index e2431fd4f05..797f31482ce 100644 --- a/Flow.Launcher/Helper/ErrorReporting.cs +++ b/Flow.Launcher/Helper/ErrorReporting.cs @@ -25,12 +25,6 @@ private static void Report(Exception e, bool silent = false, [CallerMemberName] // This change modifies the behavior to log the exception instead of showing the "Error report UI". if (ExceptionHelper.IsRecoverableDwmCompositionException(e)) return; - // Workaround for a WPF issue where changing the Windows theme or accent color triggers - // SystemResources.InvalidateTreeResources, which tries to clone Color values stored in styles - // and fails with an InvalidCastException. This is a benign framework-level exception that - // does not affect Flow Launcher functionality, so we log it silently instead of showing the error dialog. - if (ExceptionHelper.IsRecoverableSystemResourceException(e)) return; - var reportWindow = new ReportWindow(e); reportWindow.Show(); } diff --git a/Flow.Launcher/Helper/ExceptionHelper.cs b/Flow.Launcher/Helper/ExceptionHelper.cs index 0cc7747ad7c..5dd57f9bbcd 100644 --- a/Flow.Launcher/Helper/ExceptionHelper.cs +++ b/Flow.Launcher/Helper/ExceptionHelper.cs @@ -39,23 +39,4 @@ internal static bool IsRecoverableDwmCompositionException(Exception exception) return !string.IsNullOrEmpty(stackTrace) && stackTrace.Contains("DwmCompositionChanged", StringComparison.OrdinalIgnoreCase); } - - /// - /// Returns true if the exception is a recoverable WPF system resource invalidation exception - /// that occurs when Windows changes its theme or accent colors. This is a known WPF issue where - /// Color values stored in styles are incorrectly cloned during resource tree invalidation. - /// - internal static bool IsRecoverableSystemResourceException(Exception exception) - { - if (exception is not InvalidCastException) - { - return false; - } - - // Check for the specific Color-to-Expression cast failure originating from WPF's - // SystemResources.InvalidateTreeResources, triggered by Windows theme/accent color changes. - var stackTrace = exception.StackTrace; - return !string.IsNullOrEmpty(stackTrace) && - stackTrace.Contains("System.Windows.SystemResources.InvalidateTreeResources", StringComparison.Ordinal); - } } From 60b92c5b969b372bd14f493effdbcee475465e0d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Feb 2026 15:14:34 +0800 Subject: [PATCH 04/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 70 +++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index c3bb6190f01..df2dd878f96 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -221,7 +221,13 @@ private static void SetFontProperties(Style style, FontFamily fontFamily, FontSt var foregroundPropertyValue = style.Setters.OfType().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 +252,37 @@ private static void SetFontProperties(Style style, FontFamily fontFamily, FontSt } } + 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 +312,15 @@ private ResourceDictionary GetResourceDictionary(string theme) queryBoxStyle.Setters.Add(new Setter(Control.FontStretchProperty, fontStretch)); var caretBrushPropertyValue = queryBoxStyle.Setters.OfType().Any(x => x.Property.Name == "CaretBrush"); - var foregroundPropertyValue = queryBoxStyle.Setters.OfType().Where(x => x.Property.Name == "Foreground") - .Select(x => x.Value).FirstOrDefault(); + var foregroundPropertyValue = queryBoxStyle.Setters.OfType().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)); @@ -674,14 +716,18 @@ private void SetBlurForWindow(string theme, BackdropTypes backdropType) if (backdropType == BackdropTypes.Mica || backdropType == BackdropTypes.MicaAlt) { windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType().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().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().FirstOrDefault(x => x.Property == Border.CornerRadiusProperty); @@ -689,7 +735,7 @@ private void SetBlurForWindow(string theme, BackdropTypes backdropType) 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); @@ -765,7 +811,7 @@ private Color GetWindowBorderStyleBackground(string theme) 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)) @@ -803,7 +849,9 @@ private void ApplyPreviewBackground(Color? bgColor = null) // 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 +865,7 @@ private void ApplyPreviewBackground(Color? bgColor = null) 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) From 56357839734d5262e2fbb65f8fcf2be9bd225ec8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Feb 2026 21:23:20 +0800 Subject: [PATCH 05/21] 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. --- Flow.Launcher/MainWindow.xaml.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 06b2dda9eed..113f1f583af 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -109,7 +109,10 @@ public MainWindow() private void ViewModel_ActualApplicationThemeChanged(object sender, ActualApplicationThemeChangedEventArgs args) { - _ = _theme.RefreshFrameAsync(); + if (_settings.ColorScheme == Constant.System) + { + _ = _theme.RefreshFrameAsync(); + } } private void OnSourceInitialized(object sender, EventArgs e) From 966bcdd5ec2a9b8bedbe66450107f34145766259 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Feb 2026 22:03:55 +0800 Subject: [PATCH 06/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 49 +++++++++++++++------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index df2dd878f96..c67f93a33ce 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -11,7 +11,6 @@ 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; @@ -638,26 +637,29 @@ private void SetResizeBoarderThickness(Thickness? effectMargin) /// public async Task RefreshFrameAsync() { - await Application.Current.Dispatcher.InvokeAsync(() => + if (Application.Current?.Dispatcher.CheckAccess() != true) { - // Get the actual backdrop type and drop shadow effect settings - var (backdropType, useDropShadowEffect) = GetActualValue(); + await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync); + 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); + } + SetBlurForWindow(_settings.Theme, backdropType); + + if (!BlurEnabled) + { + AutoDropShadow(useDropShadowEffect); + } } /// @@ -665,13 +667,16 @@ await Application.Current.Dispatcher.InvokeAsync(() => /// public async Task SetBlurForWindowAsync() { - await Application.Current.Dispatcher.InvokeAsync(() => + if (Application.Current?.Dispatcher.CheckAccess() != true) { - // Get the actual backdrop type and drop shadow effect settings - var (backdropType, _) = GetActualValue(); + await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync); + return; + } + + // Get the actual backdrop type and drop shadow effect settings + var (backdropType, _) = GetActualValue(); - SetBlurForWindow(_settings.Theme, backdropType); - }, DispatcherPriority.Render); + SetBlurForWindow(_settings.Theme, backdropType); } /// From 7b855745d962ac34f5106eb001d167dd9a3053bc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Feb 2026 22:45:23 +0800 Subject: [PATCH 07/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index c67f93a33ce..b657a59501b 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -757,13 +757,12 @@ private void SetBlurForWindow(string theme, BackdropTypes backdropType) private void AutoDropShadow(bool useDropShadowEffect) { - SetWindowCornerPreference("Default"); - RemoveDropShadowEffectFromCurrentTheme(); if (useDropShadowEffect) { if (BlurEnabled && Win32Helper.IsBackdropSupported()) { SetWindowCornerPreference("Round"); + RemoveDropShadowEffectFromCurrentTheme(); } else { @@ -776,9 +775,11 @@ private void AutoDropShadow(bool useDropShadowEffect) if (BlurEnabled && Win32Helper.IsBackdropSupported()) { SetWindowCornerPreference("Default"); + RemoveDropShadowEffectFromCurrentTheme(); } else { + SetWindowCornerPreference("Default"); RemoveDropShadowEffectFromCurrentTheme(); } } From a83da2b00f8d833b7431c308c0b4425ca9dfb355 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Feb 2026 23:08:53 +0800 Subject: [PATCH 08/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index b657a59501b..76a74b7110c 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -99,12 +99,6 @@ private void MakeSureThemeDirectoriesExist() 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)) @@ -112,7 +106,20 @@ private void UpdateResourceDictionary(ResourceDictionary dictionaryToUpdate) 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; + } } /// @@ -654,7 +661,9 @@ public async Task RefreshFrameAsync() { 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) { From 84ed1fa1e1cc23aa4c776762a52cef0ced21451f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Feb 2026 23:22:06 +0800 Subject: [PATCH 09/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 76a74b7110c..be849228a9e 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -644,7 +644,8 @@ private void SetResizeBoarderThickness(Thickness? effectMargin) /// public async Task RefreshFrameAsync() { - if (Application.Current?.Dispatcher.CheckAccess() != true) + if (Application.Current == null) return; + if (!Application.Current.Dispatcher.CheckAccess()) { await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync); return; @@ -676,9 +677,10 @@ public async Task RefreshFrameAsync() /// public async Task SetBlurForWindowAsync() { - if (Application.Current?.Dispatcher.CheckAccess() != true) + if (Application.Current == null) return; + if (!Application.Current.Dispatcher.CheckAccess()) { - await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync); + await Application.Current?.Dispatcher.InvokeAsync(SetBlurForWindowAsync); return; } From 340f2cbc04b859b40fb6d93baf6da01336b901da Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 12:19:26 +0800 Subject: [PATCH 10/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index be849228a9e..1b6b016bd08 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -467,20 +467,17 @@ public bool ChangeTheme(string theme = null) 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 = IsBlurTheme(dict); // Apply blur and drop shadow effect so that we do not need to call it again _ = RefreshFrameAsync(); @@ -991,14 +988,12 @@ private void ColorizeWindow(string theme, BackdropTypes backdropType) } } - private static bool IsBlurTheme() + private static bool IsBlurTheme(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 From 5b6b7e728eb655b13da64e5b18416027702bcfb8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 13:59:15 +0800 Subject: [PATCH 11/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 1b6b016bd08..43814800b26 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -130,8 +130,8 @@ public void UpdateFonts() 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); From 9943b728390533a90a6024e59bbf5111d2ff3baa Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 13:59:32 +0800 Subject: [PATCH 12/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 132 ++++++++++++++++++--------- 1 file changed, 90 insertions(+), 42 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 43814800b26..e0f3368f8a5 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -522,76 +522,124 @@ public bool ChangeTheme(string theme = null) public void AddDropShadowEffectToCurrentTheme() { - var dict = GetCurrentResourceDictionary(); + // Get current theme's WindowBorderStyle + var theme = _settings.Theme; + var dict = GetThemeResourceDictionary(theme); + if (dict["WindowBorderStyle"] is not Style windowBorderStyle) return; - var windowBorderStyle = dict["WindowBorderStyle"] as Style; + // Get a new unsealed style based on the old one, and copy Resources and Triggers + var newWindowBorderStyle = GetNewWindowBorderStyle(windowBorderStyle); - var effectSetter = new Setter + // Identify existing Margin to calculate new Margin, and copy other setters + Setter existingMarginSetter = null; + foreach (var setterBase in windowBorderStyle.Setters) { - Property = UIElement.EffectProperty, - Value = new DropShadowEffect + if (setterBase is Setter setter) { - Opacity = 0.3, - ShadowDepth = 12, - Direction = 270, - BlurRadius = 30 + // 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; } - }; - 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); + // Add other setters (e.g. Background, BorderThickness) + newWindowBorderStyle.Setters.Add(setterBase); + } - SetResizeBoarderThickness(margin); + // Calculate new Margin + Thickness newMargin; + if (existingMarginSetter == null) + { + newMargin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin); } else { - var baseMargin = (Thickness)marginSetter.Value; - var newMargin = new Thickness( + var baseMargin = (Thickness)existingMarginSetter.Value; + newMargin = new Thickness( baseMargin.Left + ShadowExtraMargin, baseMargin.Top + ShadowExtraMargin, baseMargin.Right + ShadowExtraMargin, baseMargin.Bottom + ShadowExtraMargin); - marginSetter.Value = newMargin; - - SetResizeBoarderThickness(newMargin); } - windowBorderStyle.Setters.Add(effectSetter); + // Add new Margin Setter + newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, newMargin)); - UpdateResourceDictionary(dict); + // Add Drop Shadow Effect Setter + newWindowBorderStyle.Setters.Add(new Setter + { + Property = UIElement.EffectProperty, + Value = new DropShadowEffect + { + Opacity = 0.3, + ShadowDepth = 12, + Direction = 270, + BlurRadius = 30 + } + }); + + SetResizeBoarderThickness(newMargin); + + _oldResource["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); + if (dict["WindowBorderStyle"] is not Style windowBorderStyle) 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 = GetNewWindowBorderStyle(windowBorderStyle); - 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); + _oldResource["WindowBorderStyle"] = newWindowBorderStyle; + } + + private static Style GetNewWindowBorderStyle(Style windowBorderStyle) + { + // Create a new unsealed style based on the old one + var newWindowBorderStyle = new Style(windowBorderStyle.TargetType, windowBorderStyle.BasedOn); + + // Copy Resources and Triggers + foreach (var key in windowBorderStyle.Resources.Keys) + newWindowBorderStyle.Resources.Add(key, windowBorderStyle.Resources[key]); + + foreach (var trigger in windowBorderStyle.Triggers) + newWindowBorderStyle.Triggers.Add(trigger); + + return newWindowBorderStyle; } public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize) From 3db201f4a3c540e40cdefab84c1f812db25f5910 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:00:07 +0800 Subject: [PATCH 13/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index e0f3368f8a5..b7b137d914e 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -16,6 +16,7 @@ using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; using Microsoft.Win32; +using System.Windows.Threading; namespace Flow.Launcher.Core.Resource { @@ -692,7 +693,7 @@ public async Task RefreshFrameAsync() if (Application.Current == null) return; if (!Application.Current.Dispatcher.CheckAccess()) { - await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync); + await Application.Current?.Dispatcher.InvokeAsync(RefreshFrameAsync, DispatcherPriority.Render); return; } @@ -725,7 +726,7 @@ public async Task SetBlurForWindowAsync() if (Application.Current == null) return; if (!Application.Current.Dispatcher.CheckAccess()) { - await Application.Current?.Dispatcher.InvokeAsync(SetBlurForWindowAsync); + await Application.Current?.Dispatcher.InvokeAsync(SetBlurForWindowAsync, DispatcherPriority.Render); return; } From c87f3d3447a186e12450c9565f7c1f33d1e264ed Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:05:36 +0800 Subject: [PATCH 14/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index b7b137d914e..1e891745da8 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -526,7 +526,8 @@ public void AddDropShadowEffectToCurrentTheme() // Get current theme's WindowBorderStyle var theme = _settings.Theme; var dict = GetThemeResourceDictionary(theme); - if (dict["WindowBorderStyle"] is not Style windowBorderStyle) return; + var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null; + if (windowBorderStyle == null) return; // Get a new unsealed style based on the old one, and copy Resources and Triggers var newWindowBorderStyle = GetNewWindowBorderStyle(windowBorderStyle); @@ -594,7 +595,8 @@ public void RemoveDropShadowEffectFromCurrentTheme() // Get current theme's WindowBorderStyle var theme = _settings.Theme; var dict = GetThemeResourceDictionary(theme); - if (dict["WindowBorderStyle"] is not Style windowBorderStyle) return; + var windowBorderStyle = dict.Contains("WindowBorderStyle") ? dict["WindowBorderStyle"] as Style : null; + if (windowBorderStyle == null) return; // Get a new unsealed style based on the old one, and copy Resources and Triggers var newWindowBorderStyle = GetNewWindowBorderStyle(windowBorderStyle); @@ -855,8 +857,9 @@ private static void SetWindowCornerPreference(string cornerType) // 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() @@ -877,9 +880,9 @@ private Color GetWindowBorderStyleBackground(string theme) 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; From 2e6fb61248d1df56a3a97648b56102b1bb7066e1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:13:32 +0800 Subject: [PATCH 15/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 1e891745da8..ca30d6b5b6f 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -478,7 +478,7 @@ public bool ChangeTheme(string theme = null) // Check if blur is enabled var dict = GetThemeResourceDictionary(theme); - BlurEnabled = IsBlurTheme(dict); + BlurEnabled = Win32Helper.IsBackdropSupported() && IsThemeBlurEnabled(dict); // Apply blur and drop shadow effect so that we do not need to call it again _ = RefreshFrameAsync(); @@ -773,7 +773,7 @@ private void SetBlurForWindow(string theme, BackdropTypes backdropType) 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 @@ -955,13 +955,13 @@ private void ColorizeWindow(string theme, BackdropTypes backdropType) 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; + 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); @@ -1020,8 +1020,8 @@ private void ColorizeWindow(string theme, BackdropTypes backdropType) 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; @@ -1040,11 +1040,8 @@ private void ColorizeWindow(string theme, BackdropTypes backdropType) } } - private static bool IsBlurTheme(ResourceDictionary dict) + private static bool IsThemeBlurEnabled(ResourceDictionary dict) { - if (!Win32Helper.IsBackdropSupported()) // Windows 11 미만이면 무조건 false - return false; - return dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool enabled && enabled; } From 428a7ad0c25bf1903a9cf469ccd8751a5d725115 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:15:15 +0800 Subject: [PATCH 16/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index ca30d6b5b6f..099d4678084 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -963,12 +963,8 @@ private void ColorizeWindow(string theme, BackdropTypes backdropType) // Check the user's ColorScheme setting 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 - 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") @@ -983,11 +979,20 @@ private void ColorizeWindow(string theme, BackdropTypes backdropType) { // 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 From 4767b444ec4c2efc1643dc06ff8c2bd1c65711e2 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:16:36 +0800 Subject: [PATCH 17/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 099d4678084..3a34b867a48 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -1036,11 +1036,15 @@ private void ColorizeWindow(string theme, BackdropTypes backdropType) // 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; } } } From e3d71f94efa5d1fa494ad3c62c430e4fb2534c83 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:23:57 +0800 Subject: [PATCH 18/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 3a34b867a48..a1fc93b3165 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -530,7 +530,8 @@ public void AddDropShadowEffectToCurrentTheme() if (windowBorderStyle == null) return; // Get a new unsealed style based on the old one, and copy Resources and Triggers - var newWindowBorderStyle = GetNewWindowBorderStyle(windowBorderStyle); + var newWindowBorderStyle = new Style(typeof(Border)); + CopyStyle(windowBorderStyle, newWindowBorderStyle); // Identify existing Margin to calculate new Margin, and copy other setters Setter existingMarginSetter = null; @@ -599,7 +600,8 @@ public void RemoveDropShadowEffectFromCurrentTheme() if (windowBorderStyle == null) return; // Get a new unsealed style based on the old one, and copy Resources and Triggers - var newWindowBorderStyle = GetNewWindowBorderStyle(windowBorderStyle); + var newWindowBorderStyle = new Style(typeof(Border)); + CopyStyle(windowBorderStyle, newWindowBorderStyle); // Copy Setters, excluding the Effect setter and updating the Margin setter foreach (var setterBase in windowBorderStyle.Setters) @@ -630,21 +632,6 @@ public void RemoveDropShadowEffectFromCurrentTheme() _oldResource["WindowBorderStyle"] = newWindowBorderStyle; } - private static Style GetNewWindowBorderStyle(Style windowBorderStyle) - { - // Create a new unsealed style based on the old one - var newWindowBorderStyle = new Style(windowBorderStyle.TargetType, windowBorderStyle.BasedOn); - - // Copy Resources and Triggers - foreach (var key in windowBorderStyle.Resources.Keys) - newWindowBorderStyle.Resources.Add(key, windowBorderStyle.Resources[key]); - - foreach (var trigger in windowBorderStyle.Triggers) - newWindowBorderStyle.Triggers.Add(trigger); - - return newWindowBorderStyle; - } - public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize) { if (fixedWindowSize) From 46cecb2acc6363aee49dc50d360c841a58c6d8ad Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:26:12 +0800 Subject: [PATCH 19/21] Improve code quality --- Flow.Launcher.Core/Resource/Theme.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index a1fc93b3165..fcd56f2d8bb 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -697,6 +697,7 @@ public async Task RefreshFrameAsync() { 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 From 65042d9fe149bf9162380ea7e4705b7b512679a6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 1 Mar 2026 14:28:53 +0800 Subject: [PATCH 20/21] 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. --- Flow.Launcher.Core/Resource/Theme.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index fcd56f2d8bb..77e1ea5b5bd 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -588,7 +588,7 @@ public void AddDropShadowEffectToCurrentTheme() SetResizeBoarderThickness(newMargin); - _oldResource["WindowBorderStyle"] = newWindowBorderStyle; + Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle; } public void RemoveDropShadowEffectFromCurrentTheme() @@ -629,7 +629,7 @@ public void RemoveDropShadowEffectFromCurrentTheme() SetResizeBoarderThickness(null); - _oldResource["WindowBorderStyle"] = newWindowBorderStyle; + Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle; } public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize) From 7476cdd8944d4c160aeec5f025ff12bcb723bc7c Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Mon, 2 Mar 2026 18:04:00 +0800 Subject: [PATCH 21/21] Simplify theme refresh logic in ActualApplicationThemeChanged --- Flow.Launcher/MainWindow.xaml.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 113f1f583af..06b2dda9eed 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -109,10 +109,7 @@ public MainWindow() private void ViewModel_ActualApplicationThemeChanged(object sender, ActualApplicationThemeChangedEventArgs args) { - if (_settings.ColorScheme == Constant.System) - { - _ = _theme.RefreshFrameAsync(); - } + _ = _theme.RefreshFrameAsync(); } private void OnSourceInitialized(object sender, EventArgs e)