Skip to content
Draft
Changes from all commits
Commits
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
121 changes: 79 additions & 42 deletions Flow.Launcher.Core/Resource/Theme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -477,76 +477,113 @@

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);
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This re-adds setters that were already copied by CopyStyle, creating duplicate property setters in WindowBorderStyle.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Flow.Launcher.Core/Resource/Theme.cs, line 508:

<comment>This re-adds setters that were already copied by `CopyStyle`, creating duplicate property setters in `WindowBorderStyle`.</comment>

<file context>
@@ -477,76 +477,113 @@ public bool ChangeTheme(string theme = null)
-                };
-                windowBorderStyle.Setters.Add(marginSetter);
+                // Add other setters (e.g. Background, BorderThickness)
+                newWindowBorderStyle.Setters.Add(setterBase);
+            }
 
</file context>
Fix with Cubic

}
Comment on lines +486 to +509
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CopyStyle(windowBorderStyle, newWindowBorderStyle) already copies all setters (including from BasedOn), but this method then iterates windowBorderStyle.Setters and adds most of them again. That will duplicate setters for the same properties (e.g. Background/BorderThickness), which can throw when the style is sealed/applied or lead to unpredictable overrides. Consider either (a) building a derived style with BasedOn = windowBorderStyle and only adding/replacing the Margin/Effect setters, or (b) changing CopyStyle to not copy setters and instead deep-copy Triggers/Resources (as the comment suggests).

Copilot uses AI. Check for mistakes.

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;
Comment on lines +543 to +545
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assigning Application.Current.Resources["WindowBorderStyle"] creates a top-level resource that takes precedence over the theme merged dictionary. In RefreshFrameAsync, when BlurEnabled is true, AutoDropShadow() runs before SetBlurForWindow(), so this override can prevent SetBlurForWindow from updating WindowBorderStyle (background/corner radius changes) via UpdateResourceDictionary(dict). Consider updating the theme ResourceDictionary (and calling UpdateResourceDictionary) instead of setting a top-level key, or ensure the top-level override is removed when blur/backdrop handling needs the theme style to be authoritative.

Copilot uses AI. Check for mistakes.
}

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));
Comment on lines +569 to +577
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t subtract shadow margin from the baseline theme margin.

At Lines 573-576, ShadowExtraMargin is subtracted from the margin loaded from GetThemeResourceDictionary(theme) (baseline style). If a theme already defines a margin, disabling shadow shrinks it (and can go negative).

✅ Minimal fix
- var newMargin = new Thickness(
-     currentMargin.Left - ShadowExtraMargin,
-     currentMargin.Top - ShadowExtraMargin,
-     currentMargin.Right - ShadowExtraMargin,
-     currentMargin.Bottom - ShadowExtraMargin);
- newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, newMargin));
+ newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, currentMargin));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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));
if (setter.Property == FrameworkElement.MarginProperty)
{
var currentMargin = (Thickness)setter.Value;
newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, currentMargin));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Core/Resource/Theme.cs` around lines 569 - 577, The code is
incorrectly subtracting ShadowExtraMargin from any theme-defined margin (in the
block handling setter.Property == FrameworkElement.MarginProperty), which
shrinks or can make margins negative when shadows are disabled; instead,
preserve the theme's original margin by adding the existing setter.Value
(currentMargin) to newWindowBorderStyle.Setters unchanged (i.e., remove the
subtraction/creation of newMargin and simply call
newWindowBorderStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty,
setter.Value))); keep this change local to the margin-handling branch that
processes setters from GetThemeResourceDictionary(theme).

continue;
}
}
newWindowBorderStyle.Setters.Add(setterBase);
}
Comment on lines +556 to 582
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in Add: CopyStyle already clones setters, but this loop adds the original setterBase instances again (and also adds a new Margin setter when a Margin exists). This will create duplicate setters for the same properties and may throw when the style is applied. Prefer new Style(windowBorderStyle.TargetType) { BasedOn = windowBorderStyle } and then add overrides/removals, or otherwise ensure setters are only present once.

Copilot uses AI. Check for mistakes.

SetResizeBoarderThickness(null);

UpdateResourceDictionary(dict);
Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same resource-precedence problem as in Add: setting Application.Current.Resources["WindowBorderStyle"] here can mask updates that happen later via UpdateResourceDictionary(dict) (e.g., blur/backdrop adjustments in SetBlurForWindow). If this method is intended to restore the theme-defined style, consider removing the top-level override key (or updating the merged dictionary entry) rather than always re-setting it at the application root.

Suggested change
Application.Current.Resources["WindowBorderStyle"] = newWindowBorderStyle;
// Update the current theme's resource dictionary instead of overriding the
// application-level resource, so later theme updates keep correct precedence.
dict["WindowBorderStyle"] = newWindowBorderStyle;
UpdateResourceDictionary(dict);

Copilot uses AI. Check for mistakes.
}

public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize)
Expand Down Expand Up @@ -602,7 +639,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 642 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 642 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 642 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 +683,7 @@
backdropType = BackdropTypes.None;
}

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

Check warning on line 686 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 686 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,7 +707,7 @@
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 710 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"));
Expand Down
Loading