Allow adjusting window background blur#30
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a configurable background blur style for LuminareWindow and Luminare backgrounds, including an opt-in custom blur radius that relies on private APIs.
Changes:
- Introduces
LuminareBackgroundBlurStyleand stores it inEnvironmentValues. - Extends
LuminareWindowto accept and inject a defaultbackgroundBlurStyleinto the rootLuminareView. - Updates
VisualEffectViewto optionally apply a custom blur radius and factors the tint overlay intoLuminareBackgroundTintOverlay.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| Sources/Luminare/Utilities/LuminareBackgroundBlurStyle.swift | Adds a public enum describing blur style options (regular/custom). |
| Sources/Luminare/Utilities/Extensions/View+Extensions.swift | Documents that luminareBackground() uses the blur style from the environment. |
| Sources/Luminare/Utilities/Extensions/EnvironmentValues+Extensions.swift | Adds luminareBackgroundBlurStyle environment entry with default .regular. |
| Sources/Luminare/Main Window/LuminareWindow.swift | Adds init parameter to set the blur style and injects it into the root view environment. |
| Sources/Luminare/Main Window/LuminareView.swift | Adds a custom-blur background behind the root view when .custom is selected. |
| Sources/Luminare/Components/Auxiliary/VisualEffectView.swift | Adds blurStyle support and applies a custom blur radius via private layer keys. |
| Sources/Luminare/Components/Auxiliary/Modifiers/LuminareBackgroundEffectModifier.swift | Makes luminareBackground() conditional on .regular and reuses the new tint overlay view. |
| Sources/Luminare/Components/Auxiliary/LuminareBackgroundTintOverlay.swift | New shared view for the tint overlay previously inlined in the modifier. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if blurStyle == .regular { | ||
| VisualEffectView( | ||
| material: .menu, | ||
| blendingMode: .behindWindow | ||
| ) | ||
|
|
||
| Rectangle() | ||
| .foregroundStyle(.tint) | ||
| .opacity(colorscheme == .light ? 0.025 : 0.1) | ||
| .blendMode(.multiply) | ||
| LuminareBackgroundTintOverlay() | ||
| } |
There was a problem hiding this comment.
When blurStyle is .custom, luminareBackground() now renders no background at all (the entire ZStack becomes empty). Since the PR description says .luminareBackground() pulls the style from the environment, it should also render a blur for .custom (likely by passing blurStyle into VisualEffectView) rather than becoming a no-op.
| private func applyCustomBlurIfNeeded(to view: NSVisualEffectView) { | ||
| guard case let .custom(radius) = blurStyle else { | ||
| return | ||
| } | ||
|
|
||
| view.wantsLayer = true | ||
|
|
||
| DispatchQueue.main.async { | ||
| guard let backdropLayer = backdropLayer(in: view) else { | ||
| return | ||
| } | ||
|
|
||
| backdropLayer.setValue(radius, forKeyPath: "filters.gaussianBlur.inputRadius") | ||
| } | ||
| } |
There was a problem hiding this comment.
updateNSView calls applyCustomBlurIfNeeded each time, which schedules an async block and then performs a recursive subview/sublayer search to find the backdrop layer. This can become expensive during frequent SwiftUI updates. Consider caching the resolved backdrop layer in a Coordinator (or associating it with the view once found) and only re-applying the radius when it changes, to avoid repeated tree traversal and repeated async scheduling.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This PR adds the ability for
LuminareWindowto have a custom blur style, with the default being the existing version.When initialising
LuminareWindow, you can also set a blur style fromLuminareBackgroundBlurStyle:.luminareBackground()pulls this value from the environment, and I didn't make it locally-overrideable because it doesn't make sense to change the value in one place.These two options exist because
.customuses private APIs, that are relatively stable, but nonetheless private. the.regularversion uses the existing version and is stable.