-
Notifications
You must be signed in to change notification settings - Fork 13
Allow adjusting window background blur #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // | ||
| // LuminareBackgroundTintOverlay.swift | ||
| // Luminare | ||
| // | ||
| // Created by Adon Omeri on 2025-03-24. | ||
| // | ||
|
|
||
| import SwiftUI | ||
|
|
||
| /// The tint overlay applied on top of any `VisualEffectView` inside Luminare backgrounds. | ||
| struct LuminareBackgroundTintOverlay: View { | ||
| @Environment(\.colorScheme) private var colorScheme | ||
|
|
||
| var body: some View { | ||
| Rectangle() | ||
| .foregroundStyle(.tint) | ||
| .opacity(colorScheme == .light ? 0.025 : 0.1) | ||
| .blendMode(.multiply) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,17 +7,82 @@ | |
|
|
||
| import SwiftUI | ||
|
|
||
| /// Source: https://oskargroth.com/blog/reverse-engineering-nsvisualeffectview | ||
| struct VisualEffectView: NSViewRepresentable { | ||
| let material: NSVisualEffectView.Material | ||
| let blendingMode: NSVisualEffectView.BlendingMode | ||
| let blurStyle: LuminareBackgroundBlurStyle | ||
|
|
||
| init( | ||
| material: NSVisualEffectView.Material, | ||
| blendingMode: NSVisualEffectView.BlendingMode, | ||
| blurStyle: LuminareBackgroundBlurStyle = .regular | ||
| ) { | ||
| self.material = material | ||
| self.blendingMode = blendingMode | ||
| self.blurStyle = blurStyle | ||
| } | ||
|
|
||
| func makeNSView(context _: Context) -> NSVisualEffectView { | ||
| let visualEffectView = NSVisualEffectView() | ||
| visualEffectView.material = material | ||
| visualEffectView.blendingMode = blendingMode | ||
| visualEffectView.isEmphasized = true | ||
| applyCustomBlurIfNeeded(to: visualEffectView) | ||
| return visualEffectView | ||
| } | ||
|
|
||
| func updateNSView(_: NSVisualEffectView, context _: Context) {} | ||
| func updateNSView(_ view: NSVisualEffectView, context _: Context) { | ||
| view.material = material | ||
| view.blendingMode = blendingMode | ||
| applyCustomBlurIfNeeded(to: view) | ||
| } | ||
|
|
||
| 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") | ||
| } | ||
| } | ||
|
Comment on lines
+41
to
+55
|
||
|
|
||
| private func backdropLayer(in view: NSView) -> CALayer? { | ||
| if let layer = backdropLayer(in: view.layer) { | ||
| return layer | ||
| } | ||
|
|
||
| for subview in view.subviews { | ||
| if let layer = backdropLayer(in: subview) { | ||
| return layer | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| private func backdropLayer(in layer: CALayer?) -> CALayer? { | ||
| guard let layer else { | ||
| return nil | ||
| } | ||
|
|
||
| if String(describing: type(of: layer)).contains("Backdrop") { | ||
| return layer | ||
| } | ||
|
|
||
| for sublayer in layer.sublayers ?? [] { | ||
| if let backdropLayer = backdropLayer(in: sublayer) { | ||
| return backdropLayer | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // | ||
| // LuminareBackgroundBlurStyle.swift | ||
| // Luminare | ||
| // | ||
| // Created by Adon Omeri on 2025-03-23. | ||
| // | ||
|
|
||
| import SwiftUI | ||
|
|
||
| /// Controls how `luminareBackground` and the window’s root view render their blur. | ||
| public enum LuminareBackgroundBlurStyle: Equatable, Sendable { | ||
| /// Applies a regular window material to the window. | ||
| /// - Note: This type does not use private APIs and is stable. | ||
| case regular | ||
| /// Set a custom blur level for the window. | ||
| /// - Warning: This type uses private APIs and may break in a future OS. Test on all macOS versions you are targetting. | ||
|
omeriadon marked this conversation as resolved.
Outdated
|
||
| case custom(radius: CGFloat) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
blurStyleis.custom,luminareBackground()now renders no background at all (the entireZStackbecomes empty). Since the PR description says.luminareBackground()pulls the style from the environment, it should also render a blur for.custom(likely by passingblurStyleintoVisualEffectView) rather than becoming a no-op.