diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index fb463b4d4c0..a32a59b486e 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -477,76 +477,114 @@ 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)); + ThemeHelper.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 + ThemeHelper.ReplaceSetter(newWindowBorderStyle, new Setter(FrameworkElement.MarginProperty, newMargin)); - UpdateResourceDictionary(dict); + // Add Drop Shadow Effect Setter + ThemeHelper.ReplaceSetter(newWindowBorderStyle, 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)); + ThemeHelper.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); + ThemeHelper.ReplaceSetter(newWindowBorderStyle, 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) @@ -673,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 cd6b04c53b5..1be9c6ba406 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 @@ -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);