From c3b3b5f991c555a27d1292575bbe1dcbc9f9a68d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Mar 2026 12:43:45 +0800 Subject: [PATCH 1/4] Refactor drop shadow handling for window border style Refactor drop shadow effect logic to create a new Style instance instead of modifying the existing one in-place. This approach copies all relevant resources, triggers, and setters, updating only the Margin and Effect as needed. The new Style is applied at the application level, improving robustness for theme switching and preventing issues with sealed or shared Style objects. --- Flow.Launcher.Core/Resource/Theme.cs | 121 +++++++++++++++++---------- 1 file changed, 79 insertions(+), 42 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index c3bb6190f01..0850e9f21c0 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -477,76 +477,113 @@ 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); + 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) { - 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); + + 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) From ac404666bbbb3cbecc0f2548cbb127ce3fb4469e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 9 Mar 2026 18:17:47 +0800 Subject: [PATCH 2/4] Refactor: use ThemeHelper.CopyStyle for style copying Replaced direct calls to CopyStyle with ThemeHelper.CopyStyle in Theme.cs. This centralizes style copying logic and improves code organization and maintainability. --- 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 96ff5f7edf5..669628fd917 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -485,7 +485,7 @@ public void AddDropShadowEffectToCurrentTheme() // Get a new unsealed style based on the old one, and copy Resources and Triggers var newWindowBorderStyle = new Style(typeof(Border)); - CopyStyle(windowBorderStyle, newWindowBorderStyle); + ThemeHelper.CopyStyle(windowBorderStyle, newWindowBorderStyle); // Identify existing Margin to calculate new Margin, and copy other setters Setter existingMarginSetter = null; @@ -555,7 +555,7 @@ public void RemoveDropShadowEffectFromCurrentTheme() // Get a new unsealed style based on the old one, and copy Resources and Triggers var newWindowBorderStyle = new Style(typeof(Border)); - CopyStyle(windowBorderStyle, newWindowBorderStyle); + ThemeHelper.CopyStyle(windowBorderStyle, newWindowBorderStyle); // Copy Setters, excluding the Effect setter and updating the Margin setter foreach (var setterBase in windowBorderStyle.Setters) From 2bf6e2634c2e0d32445ca8d4f38c0dc10d2272e3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 9 Mar 2026 18:17:50 +0800 Subject: [PATCH 3/4] Simplify style copying by setting BasedOn directly Refactored ThemeHelper.CopyStyle to assign the target style's BasedOn property directly from the original style, instead of recursively copying base style properties. This reduces complexity and ensures correct style inheritance. --- Flow.Launcher.Core/Resource/ThemeHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/ThemeHelper.cs b/Flow.Launcher.Core/Resource/ThemeHelper.cs index cd6b04c53b5..bf89cdd1d1c 100644 --- a/Flow.Launcher.Core/Resource/ThemeHelper.cs +++ b/Flow.Launcher.Core/Resource/ThemeHelper.cs @@ -8,10 +8,10 @@ public static class ThemeHelper { public 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, use the same base style for the target style if (originalStyle.BasedOn != null) { - CopyStyle(originalStyle.BasedOn, targetStyle); + targetStyle.BasedOn = originalStyle.BasedOn; } // Copy the setters from the original style From 3c8266126268f4e0aa6144cbcb6f331d6412bfb8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 9 Mar 2026 18:54:10 +0800 Subject: [PATCH 4/4] Remove duplicate Margin and Effect setters in theme code Ensure existing Margin and Effect setters are removed from newWindowBorderStyle before adding new ones. This prevents duplicate property setters and ensures only the latest values are applied. --- Flow.Launcher.Core/Resource/Theme.cs | 13 ++++++------- Flow.Launcher.Core/Resource/ThemeHelper.cs | 9 +++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 669628fd917..a32a59b486e 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -525,10 +525,10 @@ public void AddDropShadowEffectToCurrentTheme() } // Add new Margin Setter - newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, newMargin)); + ThemeHelper.ReplaceSetter(newWindowBorderStyle, new Setter(FrameworkElement.MarginProperty, newMargin)); // Add Drop Shadow Effect Setter - newWindowBorderStyle.Setters.Add(new Setter + ThemeHelper.ReplaceSetter(newWindowBorderStyle, new Setter { Property = UIElement.EffectProperty, Value = new DropShadowEffect @@ -574,10 +574,11 @@ public void RemoveDropShadowEffectFromCurrentTheme() currentMargin.Top - ShadowExtraMargin, currentMargin.Right - ShadowExtraMargin, currentMargin.Bottom - ShadowExtraMargin); - newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, newMargin)); + ThemeHelper.ReplaceSetter(newWindowBorderStyle, new Setter(FrameworkElement.MarginProperty, newMargin)); continue; } } + newWindowBorderStyle.Setters.Add(setterBase); } @@ -710,13 +711,11 @@ private void SetBlurForWindow(string theme, BackdropTypes backdropType) // If the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent if (backdropType is BackdropTypes.Mica or BackdropTypes.MicaAlt) { - windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType().FirstOrDefault(x => x.Property == Control.BackgroundProperty)); - windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, ThemeHelper.GetFrozenSolidColorBrush(Color.FromArgb(1, 0, 0, 0)))); + ThemeHelper.ReplaceSetter(windowBorderStyle, new Setter(Border.BackgroundProperty, ThemeHelper.GetFrozenSolidColorBrush(Color.FromArgb(1, 0, 0, 0)))); } else if (backdropType == BackdropTypes.Acrylic) { - windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType().FirstOrDefault(x => x.Property == Control.BackgroundProperty)); - windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, Brushes.Transparent)); + ThemeHelper.ReplaceSetter(windowBorderStyle, new Setter(Border.BackgroundProperty, Brushes.Transparent)); } // For themes with blur enabled, the window border is rendered by the system, so it's treated as a simple rectangle regardless of thickness. diff --git a/Flow.Launcher.Core/Resource/ThemeHelper.cs b/Flow.Launcher.Core/Resource/ThemeHelper.cs index bf89cdd1d1c..1be9c6ba406 100644 --- a/Flow.Launcher.Core/Resource/ThemeHelper.cs +++ b/Flow.Launcher.Core/Resource/ThemeHelper.cs @@ -21,6 +21,15 @@ public static void CopyStyle(Style originalStyle, Style targetStyle) } } + public static void ReplaceSetter(Style style, Setter setter) + { + var existingSetter = style.Setters.OfType().FirstOrDefault(s => s.Property == setter.Property); + if (existingSetter != null) + style.Setters.Remove(existingSetter); + + style.Setters.Add(setter); + } + public static SolidColorBrush GetFrozenSolidColorBrush(Color color) { var brush = new SolidColorBrush(color);