Skip to content
Draft
Changes from 4 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1146d53
Initial plan
Copilot Feb 27, 2026
372f142
Fix: Silently handle InvalidCastException from WPF system resource in…
Copilot Feb 27, 2026
41d5e0a
Revert "Fix: Silently handle InvalidCastException from WPF system res…
Jack251970 Feb 28, 2026
60b92c5
Improve brush handling and resource safety in theme styles
Jack251970 Feb 28, 2026
5635783
Limit theme refresh to when color scheme is "System"
Jack251970 Feb 28, 2026
966bcdd
Refactor UI thread checks in Theme async methods
Jack251970 Feb 28, 2026
7b85574
Refactor drop shadow logic in Theme.cs for accuracy
Jack251970 Feb 28, 2026
a83da2b
Improve Theme resource handling and suppress async warnings
Jack251970 Feb 28, 2026
84ed1fa
Add null checks and fix dispatcher invocation in Theme
Jack251970 Feb 28, 2026
340f2cb
Refactor blur theme detection for accuracy
Jack251970 Mar 1, 2026
5b6b7e7
Rename themeName to theme in UpdateFonts for consistency
Jack251970 Mar 1, 2026
9943b72
Refactor drop shadow handling for window border style
Jack251970 Mar 1, 2026
3db201f
Use DispatcherPriority.Render for theme UI updates
Jack251970 Mar 1, 2026
c87f3d3
Improve theme resource access robustness
Jack251970 Mar 1, 2026
2e6fb61
Refactor blur theme logic and helper function
Jack251970 Mar 1, 2026
428a7ad
Prioritize user ColorScheme over system dark mode setting
Jack251970 Mar 1, 2026
4767b44
Optimize SolidColorBrush usage for window background
Jack251970 Mar 1, 2026
e3d71f9
Refactor window border style creation and copying
Jack251970 Mar 1, 2026
46cecb2
Improve code quality
Jack251970 Mar 1, 2026
65042d9
Update WindowBorderStyle to use global app resources
Jack251970 Mar 1, 2026
7476cdd
Simplify theme refresh logic in ActualApplicationThemeChanged
Jack251970 Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 59 additions & 11 deletions Flow.Launcher.Core/Resource/Theme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,13 @@
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
{
Expand All @@ -246,6 +252,37 @@
}
}

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

Check warning on line 264 in Flow.Launcher.Core/Resource/Theme.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`freezables` is not a recognized word. (unrecognized-spelling)
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);
Expand Down Expand Up @@ -275,10 +312,15 @@
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));
Expand Down Expand Up @@ -602,7 +644,7 @@
var (backdropType, useDropShadowEffect) = GetActualValue();

// Remove OS minimizing/maximizing animation
// Methods.SetWindowAttribute(new WindowInteropHelper(mainWindow).Handle, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 3);

Check warning on line 647 in Flow.Launcher.Core/Resource/Theme.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`FORCEDISABLED` is not a recognized word. (unrecognized-spelling)

Check warning on line 647 in Flow.Launcher.Core/Resource/Theme.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`DWMWA` is not a recognized word. (unrecognized-spelling)

Check warning on line 647 in Flow.Launcher.Core/Resource/Theme.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`DWMWINDOWATTRIBUTE` is not a recognized word. (unrecognized-spelling)

// The timing of adding the shadow effect should vary depending on whether the theme is transparent.
if (BlurEnabled)
Expand Down Expand Up @@ -646,7 +688,7 @@
backdropType = BackdropTypes.None;
}

// Dropshadow on and control disabled.(user can't change dropshadow with blur theme)

Check warning on line 691 in Flow.Launcher.Core/Resource/Theme.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`dropshadow` is not a recognized word. (unrecognized-spelling)

Check warning on line 691 in Flow.Launcher.Core/Resource/Theme.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Dropshadow` is not a recognized word. (unrecognized-spelling)
if (BlurEnabled)
{
useDropShadowEffect = true;
Expand All @@ -670,26 +712,30 @@
bool hasBlur = dict.Contains("ThemeBlurEnabled") && dict["ThemeBlurEnabled"] is bool b && b;
if (BlurEnabled && hasBlur && Win32Helper.IsBackdropSupported())
{
// If the BackdropType is Mica or MicaAlt, set the windowborderstyle's background to transparent

Check warning on line 715 in Flow.Launcher.Core/Resource/Theme.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`windowborderstyle` is not a recognized word. (unrecognized-spelling)
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);
if (cornerRadiusSetter != null)
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);
Expand Down Expand Up @@ -765,7 +811,7 @@
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))
Expand Down Expand Up @@ -803,7 +849,9 @@

// 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.
Expand All @@ -817,7 +865,7 @@
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)
Expand Down
Loading