Skip to content

Conversation

@ahonn
Copy link
Contributor

@ahonn ahonn commented Nov 22, 2025

Fixes #198 and enhances the apply_liquid_glass API with improved layer management and customization options.

Changes

1. Fix WebView layering and corner radius issues

Resolves edge misalignment by reparenting the WebView into the glass view's contentView instead of placing it as a sibling. This ensures proper layer hierarchy and correct corner radius rendering.

2. Add opaque background option

The Glass Effect View's high transparency can be problematic. Adding a background directly in the WebView blocks the liquid glass effect. This introduces an optional opaque parameter that creates a separate NSBox background layer positioned below the glass view, preserving the glass effect while providing background control.

3. Refactor API with LiquidGlassOptions struct

Breaking Change: Consolidate parameters into a structured options object for better ergonomics.

Before:

apply_liquid_glass(&window, style, tint_color, radius)

After:

apply_liquid_glass(&window, LiquidGlassOptions {
    style: NSGlassEffectViewStyle::Clear,
    radius: Some(26.0),
    opaque: Some(false),
    ..Default::default()
})

@FabianLars
Copy link
Member

Resolves edge misalignment by reparenting the WebView into the glass view's contentView instead of placing it as a sibling. This ensures proper layer hierarchy and correct corner radius rendering.

The reason why i didn't copy your changes into this repo yet is because i don't think we should have webview specific code here. If possible i'd rather expose an option for the contentView and let users specify the webview themselves.
Most users won't even see this since we do that for them in Tauri.

This introduces an optional opaque parameter that creates a separate NSBox background layer positioned below the glass view, preserving the glass effect while providing background control.

I'll try to check it out later but what glass effect is left when you have an opaque background and what's the difference from a opaque webview background (eg either wkwebview's "native" background or just the html/css color)

Refactor API with LiquidGlassOptions struct

I don't think this fits the style of this crate tbh.

@ahonn
Copy link
Contributor Author

ahonn commented Nov 22, 2025

The reason why i didn't copy your changes into this repo yet is because i don't think we should have webview specific code here. If possible i'd rather expose an option for the contentView and let users specify the webview themselves.
Most users won't even see this since we do that for them in Tauri.

I agree that having WebView-specific logic inside this crate isn’t ideal. My implementation was mainly a pragmatic fix for my own app, and I don’t think it’s necessarily the right abstraction. Exposing a contentView option and letting users attach their own WebView sounds like the cleaner long-term approach, and I’m happy to adjust the PR in that direction.

I'll try to check it out later but what glass effect is left when you have an opaque background and what's the difference from a opaque webview background (eg either wkwebview's "native" background or just the html/css color)

Based on my tests, an opaque WebView background blocks the glass effect completely. But an opaque layer under the glass still preserves the system tinting. You can verify this easily by moving the window over a strong-colored background, the glass keeps its subtle tint, while an opaque WebView background removes it.

I don't think this fits the style of this crate tbh.

That makes sense. I agree it doesn’t fully match the crate’s current style. I only grouped the parameters because there were getting to be a few, but I’m completely fine reverting to the original API approach.

@FabianLars
Copy link
Member

Exposing a contentView option and letting users attach their own WebView sounds like the cleaner long-term approach, and I’m happy to adjust the PR in that direction.

awesome, that would be appreciated :)

Based on my tests, an opaque WebView background blocks the glass effect completely. But an opaque layer under the glass still preserves the system tinting. You can verify this easily by moving the window over a strong-colored background, the glass keeps its subtle tint, while an opaque WebView background removes it.

got it, sounds reasonable then.

That makes sense. I agree it doesn’t fully match the crate’s current style. I only grouped the parameters because there were getting to be a few, but I’m completely fine reverting to the original API approach.

If we keep the opaque option and add a contentView option then it's indeed getting a bit much and i'm okay with keeping the options in that case though i'd like to hear @amrbashir opinion on that if he has one.

@amrbashir
Copy link
Member

I don't mind changing the API, we can even extend the same pattern to other platforms as well. My only concern is proofing against future breaking changes, so I would suggest to not make the structs fields public and provide a builder-pattern struct (or just methods on same struct).

@ahonn
Copy link
Contributor Author

ahonn commented Nov 25, 2025

Thanks for the suggestions!

I've exposed a generic content_view() option instead. This keeps the crate framework-agnostic while still solving the layering issue. For the API style, I've converted options to use the builder pattern as @amrbashir suggested. The WebView detection logic has now been moved to the tauri example as a reference for users who need it.

}

unsafe fn apply_corner_radius_layer(view: &NSView, radius: f64) {
let _: () = msg_send![view, setWantsLayer: true];
Copy link
Member

Choose a reason for hiding this comment

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

just one example but most msg_send uses can be replaced with an explicit objc2 function, for example here it's

view.setWantsLayer(true);

ref https://docs.rs/objc2-app-kit/latest/objc2_app_kit/struct.NSView.html#method.setWantsLayer

that should make the code a bit nicer to look at x)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I’ve updated the code to use the explicit objc2 APIs instead of msg_send! Here and in the other spots as well.

Copy link
Member

@FabianLars FabianLars left a comment

Choose a reason for hiding this comment

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

didn't look through everything yet cause my battery is almost empty x)

}

unsafe fn apply_corner_radius_layer(view: &NSView, radius: f64) {
view.setWantsLayer(true);
Copy link
Member

Choose a reason for hiding this comment

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

we have to monitor this very closely. https://developer.apple.com/documentation/appkit/nsview/wantslayer sounds like it could cause conflicts with idk what we're doing elsewhere in tauri land.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated it only to call setWantsLayer(true) when the view doesn’t already have a layer, and then modify the existing layer. Not entirely sure if this is the safest approach, so let me know if we should handle it differently.

Copy link
Member

Choose a reason for hiding this comment

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

yeah idk either, this wasn't really a call for action for you. just in case something acts weird (eg with tauri's unstable feature flag) we know to have this in mind 🤷

self
}

pub fn content_view(mut self, view: NonNull<c_void>) -> Self {
Copy link
Member

Choose a reason for hiding this comment

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

Let's just take an NSView directly here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've replaced all NonNull<c_void> with &NSView.
Maybe we need to update apply_vibrancy and clear_vibrancy, or their parameter types will be inconsistent.

Copy link
Member

Choose a reason for hiding this comment

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

good call

}

pub unsafe fn apply_liquid_glass(
ns_view: NonNull<c_void>,
Copy link
Member

Choose a reason for hiding this comment

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

i know this is my bad but i think we can take an NSView here as well - the NonNull thing is from before we had the objc2 crates

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I've considered this as well. I will change this to NSView.

Ok(())
}

pub unsafe fn clear_liquid_glass(ns_view: NonNull<c_void>) -> Result<bool, Error> {
Copy link
Member

Choose a reason for hiding this comment

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

same

@socket-security
Copy link

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​serde@​1.0.2288110093100100
Addedcargo/​serde_json@​1.0.1458210093100100
Addedcargo/​tauri@​2.9.385100100100100
Addedcargo/​tauri-build@​2.5.29610093100100

View full report

@ahonn ahonn requested a review from FabianLars November 28, 2025 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MacOS apply_liquid_glass breaks window edges

3 participants