Hi! Has anyone made a blur semi-transparent backgr...
# compose-desktop
y
Hi! Has anyone made a blur semi-transparent background? I try to mimc MacOS tray apps and the standard dialogs from the tray. They have a transparent background with blur effect. 1. I’ve found this thread, which references a good article on how to do that with Skia. But it looks like too much of ceremony. 2. I have also found how to make it with
Modifier.blur()
, which is also not trivial. Maybe something has changed and now it can be done simpler?
a
r
Mac native dev here. You'll have trouble doing this in a mac tray application, because behind-window blur can only be achieved using NSVisualEffectView in AppKit. It's impossible to achieve this "inside" compose, because the OS won't allow the compose renderer see the pixels behind it. If I needed to do this, I'd look up how to do this with Swing. You'll want the entire compose JPanel to be nested inside an NSVisualEffectView.
👍 2
thank you color 1
👍🏻 1
If you just want blur inside an opaque window (i.e. not seeing behind the window) then it's a lot easier because the blurring can be done entirely in compose-land.
One other thing to consider is that for a native look, NSVisualEffectView is paired with "vibrancy", where views placed inside it are dynamically recolored to optimise for contrast. Without vibrancy, your colours will appear grey or washed out. This can be toggled only at a native NSView level by subclassing NSView, so you'll likely need to lay out different bits of your app using Swing JPanels (which I believe are NSView under the hood). Basically, achieving this kind of effect on mac is extremely difficult due to the different architectures of AppKit + Compose
https://mackuba.eu/2018/07/04/dark-side-mac-1/ is a good article on some of these topics
y
@rob42 thank you for the detailed answer! Looks like I’ll just stick with transparency, so not to deal with native code. In my case, it’s not worth it.
r
Good luck! Making the panel fully opaque with a suitable light/dark bg would probably also look good too!
👍 1
k
Haze would indeed be a simpler way to do this, since it hides away the implementation difference between platforms and platform versions. But eventually, I think point 5 in the author’s conclusion is the article’s downfall. Using shaders is the way to do it, even if you need to write a few lines of code in a slightly unfamiliar language of GLSL / SkSL / AGSL.
r
Haze will not do this, because it only operates on the compose canvas, not on the window itself
So it's suitable for blurring content inside your window, but not for behind-window blurring
For further reference: The only way to achieve this effect on macos is with
NSVisualEffectView
unfortunately, because applications are unable to see the pixels behind their window. If you try to capture a transparent window into a bitmap, you'll end up with a transparent bitmap, not a bitmap of what's behind the window. It would be possible to create an NSVisualEffectView wrapper though, which adds JPanels as children of NSVisualEffectView so you get the right effect. Those panels could then be laid out in compose, but with the z-index caveats that come with that 🙂
👍 2
thank you color 1
m
Couldn’t a tray app screenshot the desktop when you click it, one frame before it shows the popup? Then it knows what’s behind it. It won’t update dynamically if the background changes, but that shouldn’t be too common.
r
The macOS permission model doesn't allow capturing windows that aren't owned by your process anymore
A few years ago this would have worked, but it's deprecated: https://developer.apple.com/documentation/coregraphics/1454595-cgdisplaycreateimageforrect Technically you can still do this, but you must use ScreenCaptureKit with the "screen recording" permission, which people aren't gonna turn on for your background blur effect 🙂
So basically, the only way to achieve this is via an AppKit NSVisualEffectView, which works directly with the window server to render the blur. Compose will never be able to implement this in-process. Personally, I'd just implement this for the whole NSWindow (make the whole content view an NSVisualEffectView) and then draw compose over the top of that. Your transparent regions then have behind-window blur. You almost never want to nest effect views anyway because it's notoriously buggy if you do so. You'd also need to use separate JPanels for anything that needs vibrancy (contrast on top of the blur).