Skip to content

Refactor drop shadow handling for window border style#4318

Draft
Jack251970 wants to merge 1 commit intodevfrom
DropShadowEffect
Draft

Refactor drop shadow handling for window border style#4318
Jack251970 wants to merge 1 commit intodevfrom
DropShadowEffect

Conversation

@Jack251970
Copy link
Member

@Jack251970 Jack251970 commented Mar 4, 2026

CHANGES

Refactor drop shadow effect logic to create a new Style instance instead of modifying the existing one in-place.

Separated from #4302.

TEST

  • Query window shadow effect still works.

Summary by cubic

Refactored drop shadow handling to create a new WindowBorderStyle per change instead of modifying the existing style in-place. This avoids sealed/shared style issues, improves theme switching, and keeps margin and effect updates consistent.

  • Summary of changes
    • Changed: Build a new Style(Border) from the current WindowBorderStyle using CopyStyle, then add a single Effect setter and a recalculated Margin. Apply via Application.Current.Resources["WindowBorderStyle"]. Use GetThemeResourceDictionary(_settings.Theme) instead of GetCurrentResourceDictionary. Update SetResizeBoarderThickness with the new margin (add) or null (remove).
    • Added: Logic to clone setters while skipping duplicates (Effect) and replace Margin safely. Margin math now preserves existing values and adds/subtracts ShadowExtraMargin in add/remove flows.
    • Removed: In-place mutation of style setters and UpdateResourceDictionary calls. Direct removal of the Effect setter is replaced by cloning and excluding it.
    • Memory: Minimal extra allocations for new Style and DropShadowEffect per change. Old styles are replaced and eligible for GC. No sustained memory growth expected.
    • Security: No security impact. UI-only changes, no new inputs or permissions.
    • Unit tests: No new unit tests added. Manual verification recommended for theme switching and shadow add/remove behavior.

Written for commit c3b3b5f. Summary will update on new commits.

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.
Copilot AI review requested due to automatic review settings March 4, 2026 04:44
@prlabeler prlabeler bot added Code Refactor enhancement New feature or request labels Mar 4, 2026
@github-actions github-actions bot added this to the 2.2.0 milestone Mar 4, 2026
@gitstream-cm
Copy link

gitstream-cm bot commented Mar 4, 2026

🥷 Code experts: jjw24

jjw24 has most 👩‍💻 activity in the files.
jjw24 has most 🧠 knowledge in the files.

See details

Flow.Launcher.Core/Resource/Theme.cs

Activity based on git-commit:

jjw24
MAR
FEB
JAN
DEC
NOV
OCT

Knowledge based on git-blame:
jjw24: 99%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

@gitstream-cm
Copy link

gitstream-cm bot commented Mar 4, 2026

Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX.

@coderabbitai coderabbitai bot removed the enhancement New feature or request label Mar 4, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors how the WindowBorderStyle drop shadow is applied/removed by attempting to create a new Style instance rather than mutating the existing one in-place.

Changes:

  • Reworked AddDropShadowEffectToCurrentTheme to construct a new WindowBorderStyle and add Margin + DropShadowEffect setters.
  • Reworked RemoveDropShadowEffectFromCurrentTheme to construct a new WindowBorderStyle without the Effect setter and with adjusted Margin.
  • Switched from updating the merged theme ResourceDictionary to assigning Application.Current.Resources["WindowBorderStyle"].

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +556 to 582
// 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);
}
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.
Comment on lines +543 to +545
SetResizeBoarderThickness(newMargin);

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.

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.
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.
Comment on lines +486 to +509
// 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

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.
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Flow.Launcher.Core/Resource/Theme.cs">

<violation number="1" location="Flow.Launcher.Core/Resource/Theme.cs:508">
P2: This re-adds setters that were already copied by `CopyStyle`, creating duplicate property setters in `WindowBorderStyle`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

};
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

@Jack251970 Jack251970 enabled auto-merge March 4, 2026 04:49
@prlabeler prlabeler bot added the enhancement New feature or request label Mar 4, 2026
@Jack251970 Jack251970 marked this pull request as draft March 4, 2026 04:51
auto-merge was automatically disabled March 4, 2026 04:51

Pull request was converted to draft

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

📝 Walkthrough

Walkthrough

The pull request refactors drop shadow management in Theme.cs to operate on theme-specific resource dictionaries instead of the global one. The ChangeTheme methods now reconstruct unsealed WindowBorderStyle objects by copying resources and triggers, manage margins through dedicated setters, and add/remove DropShadowEffect via style properties rather than direct mutations.

Changes

Cohort / File(s) Summary
Drop Shadow & Theme Style Management
Flow.Launcher.Core/Resource/Theme.cs
Refactored drop shadow add/remove logic to use theme-specific resource dictionaries, guard for missing WindowBorderStyle, reconstruct sealed styles with proper margin and effect handling via setters, and apply updated styles to global resources.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

Suggested reviewers

  • onesounds
  • taooceros
  • jjw24
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Refactor drop shadow handling for window border style' accurately summarizes the main change: refactoring how drop shadow effects are applied to the window border style.
Description check ✅ Passed The pull request description clearly relates to the changeset - it describes the refactoring of drop shadow effect logic from in-place style mutation to creating new Style instances.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch DropShadowEffect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Flow.Launcher.Core/Resource/Theme.cs`:
- Around line 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).

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3bf9cff6-290e-49a0-a770-d5eefac7da26

📥 Commits

Reviewing files that changed from the base of the PR and between dda9000 and c3b3b5f.

📒 Files selected for processing (1)
  • Flow.Launcher.Core/Resource/Theme.cs

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Code Refactor enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants