diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index c3bb6190f..be849228a 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -11,7 +11,6 @@ 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; @@ -100,12 +99,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 +106,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; + } } /// @@ -221,7 +227,13 @@ namespace Flow.Launcher.Core.Resource 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 +258,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 +318,15 @@ namespace Flow.Launcher.Core.Resource 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)); @@ -596,26 +644,32 @@ namespace Flow.Launcher.Core.Resource /// 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); + 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); + } } /// @@ -623,13 +677,17 @@ namespace Flow.Launcher.Core.Resource /// 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); + return; + } - SetBlurForWindow(_settings.Theme, backdropType); - }, DispatcherPriority.Render); + // Get the actual backdrop type and drop shadow effect settings + var (backdropType, _) = GetActualValue(); + + SetBlurForWindow(_settings.Theme, backdropType); } /// @@ -674,14 +732,18 @@ namespace Flow.Launcher.Core.Resource 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 +751,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 +768,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 +786,11 @@ namespace Flow.Launcher.Core.Resource if (BlurEnabled && Win32Helper.IsBackdropSupported()) { SetWindowCornerPreference("Default"); + RemoveDropShadowEffectFromCurrentTheme(); } else { + SetWindowCornerPreference("Default"); RemoveDropShadowEffectFromCurrentTheme(); } } @@ -765,7 +828,7 @@ 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)) @@ -803,7 +866,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 +882,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) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 06b2dda9e..113f1f583 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -109,7 +109,10 @@ namespace Flow.Launcher private void ViewModel_ActualApplicationThemeChanged(object sender, ActualApplicationThemeChangedEventArgs args) { - _ = _theme.RefreshFrameAsync(); + if (_settings.ColorScheme == Constant.System) + { + _ = _theme.RefreshFrameAsync(); + } } private void OnSourceInitialized(object sender, EventArgs e)