Skip to content

Freeze SolidColorBrush to improve performance#4319

Merged
Jack251970 merged 4 commits intodevfrom
FreezeResources1
Mar 9, 2026
Merged

Freeze SolidColorBrush to improve performance#4319
Jack251970 merged 4 commits intodevfrom
FreezeResources1

Conversation

@Jack251970
Copy link
Member

@Jack251970 Jack251970 commented Mar 4, 2026

Refactor background brush creation to assign, freeze, and reuse SolidColorBrush instances for better WPF performance.

Separated from #4302

Test: All types of themes work as expected.


Summary by cubic

Freezes SolidColorBrush instances, switches to type‑safe Background property checks, and moves style‑copy logic to ThemeHelper to cut allocations and improve WPF rendering. Uses Brushes.Transparent where clearer.

  • Summary of changes
    • Changed: Replace string-based setter lookup with Control.BackgroundProperty in SetBlurForWindow.
    • Changed: Use ThemeHelper.GetFrozenSolidColorBrush in SetBlurForWindow, ApplyPreviewBackground, and ColorizeWindow.
    • Changed: Call ThemeHelper.CopyStyle instead of a local method in ApplyPreviewBackground.
    • Changed: Use Brushes.Transparent for the Acrylic path.
    • Added: ThemeHelper utility with GetFrozenSolidColorBrush and CopyStyle.
    • Removed: Theme.CopyStyle method, inline unfrozen SolidColorBrush allocations, and reliance on property name strings.
    • Memory: Slight reduction in allocations and change tracking.
    • Security: No new risks.
    • Tests: No unit tests; verified via UI rendering.

Written for commit 7766d70. Summary will update on new commits.

Refactor background brush creation to assign, freeze, and reuse
SolidColorBrush instances for better WPF performance. Replace
string-based property checks with type-safe Control.BackgroundProperty
comparisons for improved robustness.
Copilot AI review requested due to automatic review settings March 4, 2026 04:49
@prlabeler prlabeler bot added Code Refactor enhancement New feature or request labels Mar 4, 2026
@Jack251970 Jack251970 enabled auto-merge March 4, 2026 04:49
@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

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

@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.

No issues found across 1 file

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

This PR aims to improve WPF performance and safety by freezing SolidColorBrush instances that are created for theme/window background rendering, reducing mutability and improving rendering efficiency.

Changes:

  • Freeze newly created SolidColorBrush instances before assigning them to Style setters in blur scenarios.
  • Freeze SolidColorBrush used for the preview window background style.
  • Freeze SolidColorBrush assigned to MainWindow.Background when blur is available.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

📝 Walkthrough

Walkthrough

Replaces property-name background checks with direct Property == Control.BackgroundProperty; extracts frozen-brush creation and style-copying into a new public ThemeHelper (methods GetFrozenSolidColorBrush and CopyStyle); replaces inline SolidColorBrush creation and local CopyStyle with ThemeHelper usage across Theme.cs.

Changes

Cohort / File(s) Summary
Theme core changes
Flow.Launcher.Core/Resource/Theme.cs
Replaced Property.Name == "Background" checks with Property == Control.BackgroundProperty. Replaced inline new SolidColorBrush(...) and local style-copy logic with ThemeHelper.GetFrozenSolidColorBrush(...) and ThemeHelper.CopyStyle(...). Adjusted Mica/MicaAlt, Acrylic, preview/background code paths to use frozen brushes.
Brush & style helper
Flow.Launcher.Core/Resource/ThemeHelper.cs
Added public static ThemeHelper with GetFrozenSolidColorBrush(Color) that returns a frozen SolidColorBrush, and CopyStyle(Style originalStyle, Style targetStyle) to duplicate styles (handles BasedOn and Setters).
Project manifest
*.csproj
Small project file modifications recorded in the manifest diff (+9/-24 lines).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • jjw24
  • taooceros
🚥 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 'Freeze SolidColorBrush to improve performance' directly and clearly summarizes the main change: freezing brush instances for better WPF performance.
Description check ✅ Passed The pull request description clearly details the refactoring work: freezing SolidColorBrush instances, replacing string-based property checks with type-safe comparisons, moving style-copy logic to ThemeHelper, and expected performance improvements.

✏️ 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 FreezeResources1

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.

🧹 Nitpick comments (1)
Flow.Launcher.Core/Resource/Theme.cs (1)

676-686: Cache constant frozen brushes instead of recreating them on each refresh.

Line 677, Line 684, and Line 926 still allocate new SolidColorBrush instances for fixed colors. Reusing class-level frozen singletons would better match the PR’s reuse/perf goal and remove repeated allocations.

♻️ Suggested refactor
+        private static readonly SolidColorBrush MicaTransparentBrush = CreateFrozenBrush(Color.FromArgb(1, 0, 0, 0));
+        private static readonly SolidColorBrush TransparentBrush = CreateFrozenBrush(Colors.Transparent);
+
+        private static SolidColorBrush CreateFrozenBrush(Color color)
+        {
+            var brush = new SolidColorBrush(color);
+            brush.Freeze();
+            return brush;
+        }

@@
-                    var brush = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
-                    brush.Freeze();
-                    windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, brush));
+                    windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, MicaTransparentBrush));
@@
-                    var brush = new SolidColorBrush(Colors.Transparent);
-                    brush.Freeze();
-                    windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, brush));
+                    windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, TransparentBrush));
@@
-                    var backgroundBrush = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
-                    backgroundBrush.Freeze();
-                    mainWindow.Background = backgroundBrush;
+                    mainWindow.Background = MicaTransparentBrush;

Also applies to: 926-934

🤖 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 676 - 686, Create and
reuse class-level frozen SolidColorBrush singletons for the fixed colors instead
of allocating new instances each refresh: add readonly static fields (e.g.,
FrozenAlmostTransparent = new SolidColorBrush(Color.FromArgb(1,0,0,0)).Freeze()
and FrozenTransparent = new SolidColorBrush(Colors.Transparent).Freeze()) and
replace the local allocations where windowBorderStyle.Setters.Add(new
Setter(Border.BackgroundProperty, brush)) is used (the blocks around the current
SolidColorBrush creations and the similar code in the region referenced at
926-934) to reference these frozen fields; ensure the fields are frozen once at
initialization and used everywhere the same constant brushes are needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Flow.Launcher.Core/Resource/Theme.cs`:
- Around line 676-686: Create and reuse class-level frozen SolidColorBrush
singletons for the fixed colors instead of allocating new instances each
refresh: add readonly static fields (e.g., FrozenAlmostTransparent = new
SolidColorBrush(Color.FromArgb(1,0,0,0)).Freeze() and FrozenTransparent = new
SolidColorBrush(Colors.Transparent).Freeze()) and replace the local allocations
where windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty,
brush)) is used (the blocks around the current SolidColorBrush creations and the
similar code in the region referenced at 926-934) to reference these frozen
fields; ensure the fields are frozen once at initialization and used everywhere
the same constant brushes are needed.

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ea83e89b-ddb2-4ede-a1ae-202bbb0172b9

📥 Commits

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

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

{
mainWindow.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
var backgroundBrush = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));
backgroundBrush.Freeze();
Copy link
Member

Choose a reason for hiding this comment

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

There a bit of repeated code, is it possible to create a new method for creating SolidColorBrush?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea👍

Introduce ThemeHelper.GetFreezeSolidColorBrush to centralize and reuse SolidColorBrush creation and freezing logic. Refactor Theme.cs to use this helper, improving code clarity and maintainability. Also, use Brushes.Transparent directly where appropriate.
@prlabeler prlabeler bot added the enhancement New feature or request label Mar 8, 2026
Moved the CopyStyle method from Theme to a new static ThemeHelper class for better code organization and reusability. Updated all references in Theme to use ThemeHelper.CopyStyle. Added ThemeHelper.cs and necessary using directives.
Renamed the method GetFreezeSolidColorBrush to GetFrozenSolidColorBrush in ThemeHelper.cs for improved naming consistency. Updated all references in Theme.cs accordingly. No changes to functionality.
@gitstream-cm
Copy link

gitstream-cm bot commented Mar 8, 2026

🥷 Code experts: no user but you matched threshold 10

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

See details

Flow.Launcher.Core/Resource/Theme.cs

Activity based on git-commit:

Jack251970
MAR
FEB
JAN
DEC
NOV
OCT 12 additions & 2 deletions

Knowledge based on git-blame:
Jack251970: 100%

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

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 676-682: The removal predicate for the existing background Setter
is using Control.BackgroundProperty but the style is for a Border, so update the
Remove calls in the windowBorderStyle manipulation (the lines handling
windowBorderStyle.Setters.Remove(...)) to check for Border.BackgroundProperty
instead of Control.BackgroundProperty so the old Border background Setter is
actually found and removed before adding the new Setter (affects the branches
using ThemeHelper.GetFreezeSolidColorBrush(...) and Brushes.Transparent when
backdropType is Acrylic).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d3244241-8734-487f-b9ae-aa19153d8337

📥 Commits

Reviewing files that changed from the base of the PR and between 9abf136 and ed14c45.

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

@coderabbitai coderabbitai bot removed the enhancement New feature or request label Mar 8, 2026
@Jack251970 Jack251970 requested a review from jjw24 March 8, 2026 15:54
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.

🧹 Nitpick comments (2)
Flow.Launcher.Core/Resource/ThemeHelper.cs (2)

24-29: This still allocates a new brush on every call.

Freeze() removes change tracking, but it does not satisfy the “reuse” part of the optimization. SetBlurForWindow, ApplyPreviewBackground, and ColorizeWindow now funnel through this helper, so repeated theme refreshes will still allocate identical brushes. Cache frozen instances by Color if the goal is to reduce allocations as well.

Possible caching approach
+using System.Collections.Concurrent;
 using System.Linq;
 using System.Windows;
 using System.Windows.Media;
@@
 public static class ThemeHelper
 {
+    private static readonly ConcurrentDictionary<Color, SolidColorBrush> FrozenBrushCache = new();
+
@@
     public static SolidColorBrush GetFrozenSolidColorBrush(Color color)
     {
-        var brush = new SolidColorBrush(color);
-        brush.Freeze();
-        return brush;
+        return FrozenBrushCache.GetOrAdd(color, static c =>
+        {
+            var brush = new SolidColorBrush(c);
+            brush.Freeze();
+            return brush;
+        });
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Core/Resource/ThemeHelper.cs` around lines 24 - 29,
GetFrozenSolidColorBrush currently creates a new SolidColorBrush on every call
which Freeze() alone doesn't prevent; change it to cache and reuse frozen
brushes keyed by Color (so SetBlurForWindow, ApplyPreviewBackground, and
ColorizeWindow receive the same frozen instance for identical colors). Implement
a static thread-safe cache (e.g., ConcurrentDictionary<Color, SolidColorBrush>
or a dictionary with a lock) inside the ThemeHelper and have
GetFrozenSolidColorBrush return an existing entry if present, otherwise create,
Freeze(), add to the cache, and return it. Ensure Color is used as the cache key
and that cache population is atomic to avoid creating duplicates under
concurrent calls.

9-22: Clarify that CopyStyle is only copying setters.

As a new shared helper, CopyStyle reads like a full Style clone, but it only flattens Setters from the BasedOn chain and skips things like Triggers, Resources, and EventSetters. Either narrow the name/API or document that contract explicitly before other call sites start using it generically.

Possible tightening
-    public static void CopyStyle(Style originalStyle, Style targetStyle)
+    /// Copies only Setter entries from `originalStyle` and its `BasedOn` chain.
+    /// Triggers, resources, and event setters are intentionally not copied.
+    public static void CopyStyle(Style originalStyle, Style targetStyle)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Flow.Launcher.Core/Resource/ThemeHelper.cs` around lines 9 - 22, The method
CopyStyle only flattens and copies Setter entries from originalStyle and its
BasedOn chain and does not copy Triggers, Resources, EventSetter instances or
other Style metadata; update the API to make this explicit by either renaming
CopyStyle to CopySettersOnly (or adding an overload CopyStyleCloneFull for a
full clone) and/or add a clear XML doc comment on the CopyStyle method stating
it only copies Setter objects (uses originalStyle.Setters.OfType<Setter>() and
targetStyle.Setters.Add) and that Triggers, Resources and EventSetter are
intentionally skipped so callers know the contract; ensure any new name or doc
mentions BasedOn flattening and reference the Setter, BasedOn, Setters,
Triggers, Resources, and EventSetter symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Flow.Launcher.Core/Resource/ThemeHelper.cs`:
- Around line 24-29: GetFrozenSolidColorBrush currently creates a new
SolidColorBrush on every call which Freeze() alone doesn't prevent; change it to
cache and reuse frozen brushes keyed by Color (so SetBlurForWindow,
ApplyPreviewBackground, and ColorizeWindow receive the same frozen instance for
identical colors). Implement a static thread-safe cache (e.g.,
ConcurrentDictionary<Color, SolidColorBrush> or a dictionary with a lock) inside
the ThemeHelper and have GetFrozenSolidColorBrush return an existing entry if
present, otherwise create, Freeze(), add to the cache, and return it. Ensure
Color is used as the cache key and that cache population is atomic to avoid
creating duplicates under concurrent calls.
- Around line 9-22: The method CopyStyle only flattens and copies Setter entries
from originalStyle and its BasedOn chain and does not copy Triggers, Resources,
EventSetter instances or other Style metadata; update the API to make this
explicit by either renaming CopyStyle to CopySettersOnly (or adding an overload
CopyStyleCloneFull for a full clone) and/or add a clear XML doc comment on the
CopyStyle method stating it only copies Setter objects (uses
originalStyle.Setters.OfType<Setter>() and targetStyle.Setters.Add) and that
Triggers, Resources and EventSetter are intentionally skipped so callers know
the contract; ensure any new name or doc mentions BasedOn flattening and
reference the Setter, BasedOn, Setters, Triggers, Resources, and EventSetter
symbols.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 764a04f9-8679-4f56-a7a5-2e2d67e6cf01

📥 Commits

Reviewing files that changed from the base of the PR and between ed14c45 and 7766d70.

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

@Jack251970 Jack251970 merged commit 0ddcc5c into dev Mar 9, 2026
16 checks passed
@Jack251970 Jack251970 deleted the FreezeResources1 branch March 9, 2026 03:12
@jjw24 jjw24 added bug Something isn't working and removed 1 min review labels Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Code Refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants