8375578: Enable translucent window backdrops#2048
Conversation
|
👋 Welcome back mfox! A progress list of the required criteria for merging this PR into |
|
❗ This change is not yet ready to be integrated. |
| * @defaultValue StageBackdrop.DEFAULT | ||
| */ | ||
| @SuppressWarnings("deprecation") | ||
| public final void initBackdrop(StageBackdrop backdrop) { |
There was a problem hiding this comment.
This API has the potential to age poorly, as enums are tricky to evolve. For example, what if a future backdrop type is configurable with a parameter? This could be improved by having the enum implement a sealed interface, so we can later add other backdrop types that can't neatly fit into an enum.
I also wonder how graceful degradation works with this API. It might be useful to be able to model a request like: give me a glass backdrop with a set of parameters, and if the platform doesn't support that, give me a TRANSIENT backdrop, and if the platform doesn't support that, I just want an opaque backdrop.
There was a problem hiding this comment.
Could perhaps the scene preferences be used for things like this? E.g. similar to having access to the platform styling keys, a user could manually override some of them to achieve the same result as with enums. That would solve any potential parameters and the degradation problem better
There was a problem hiding this comment.
I'm not sure about scene preferences, as the supported backdrop materials are not exactly a preference. It's not something users can change about the system.
How about something like this:
public sealed interface StageBackdrop {
enum Generic implements StageBackdrop {
DEFAULT,
WINDOW,
TABBED,
TRANSIENT
}
record Specialized(String name, Map<String, Object> parameters) implements StageBackdrop {
public Specialized(String name) {
this(name, Map.of());
}
}
}public class Stage {
public final void initBackdrop(StageBackdrop... backdrop) {
...
}
}The initBackdrop method would take a list of options in order of preference. Maybe I could then configure my backdrop like so:
myStage.initBackdrop(
// first choice
new StageBackdrop.Specialized(
"NSGlassEffect",
Map.of("style", "NSGlassEffectViewStyleClear")
),
// second choice
StageBackdrop.Generic.TRANSIENT
);There was a problem hiding this comment.
Should we have a (new kind of) fallback system, or should we do this with the existing Platform.isSupported(ConditionalFeature) and have the user decide based on the result of that function?
There was a problem hiding this comment.
I agree that an enum is inadequate. The two API's we have to work with (macOS and Win11) are based on enums; you specify the type of window or content (e.g. tooltip, menu, sidebar, window) and let the OS decide what material to use. macOS pro-actively enumerates a lot of different window types though it looks like many of them map to the same material under the hood. I'm not entirely sure the glass effect recently added to macOS is appropriate for JavaFX but it is a prime example of a specialized platform-specific backdrop that can't easily be squeezed into an enum.
So, yes, we should expand StageBackdrop so we can provide more information. How to do that might be beyond my pay grade (I'm not a Java expert). I don't think the existing Platform.isSupported() API would be appropriate since it takes a separate enum type.
There was a problem hiding this comment.
Should we have a (new kind of) fallback system, or should we do this with the existing
Platform.isSupported(ConditionalFeature)and have the user decide based on the result of that function?
Expanding on that question: if we have platform-specific backdrops that may have a big impact on styling and readability, stylesheets may want to query the actual backdrop used via media queries.
There was a problem hiding this comment.
The platforms seem to assume you'll draw content on the backdrop using the PlatformPreference colors (there's no way of asking the platforms for backdrop-specific color information). But it still might be good for the stylesheets to know which backdrop is in effect.
A more interesting question is whether a stylesheet can ask for a backdrop to be applied. The CSS machinery isn't set up for this; it's designed to apply styles to nodes, not windows (AFIK). And you probably don't want to apply a backdrop to, say, a menu or tooltip since the backdrop would fill the entire stage forcing it to be rectangular (no rounded corners). If the CSS called for a drop shadow the backdrop would be behind the shadow. I considered ways of fixing this but didn't come up with a workable solution.
I'll be traveling for a few days so it might be a while before I re-enter the conversation. Thanks for all the feedback!
| // Called when the window is first created. | ||
| -(void)setBackdrop:(NSVisualEffectMaterial)material | ||
| { | ||
| NSVisualEffectView* effect = [[NSVisualEffectView alloc] initWithFrame: self.bounds]; |
There was a problem hiding this comment.
Would this also work with NSGlassEffectView to get the Liquid Glass backdrop effect?
There was a problem hiding this comment.
I'm experimenting with that now. I can get it to work but it only provides a nice effect for transparent stages. And it is an entirely different beast than the other backdrops. For example, it doesn't respond to the dark/light color scheme setting and rounds the corners even on transparent stages.
|
|
||
| /** | ||
| * Specifies the backdrop for this stage. This must be done prior to | ||
| * making the stage visible. |
There was a problem hiding this comment.
Is there is a reason on why this can't be changed later on? At least in my use cases, changing the backdrop type dynamically later on works without issues
There was a problem hiding this comment.
I prototyped a version of this for Win10. On that platform we have to roll our own backdrops using DirectComposition or Windows.UI.Composition and that requires building the HWND a little differently, firing up a different painter, and re-plumbing the way pixels are delivered from Prism. I'm not entirely sure we can set all that up after the HWND is created. It's just far far easier to have the backdrop in hand from the start. (Well, all we really need to know when we create the platform window is that we'll be using a backdrop. The actual backdrop choice could be specified dynamically later but I wasn't sure what purpose that would serve.)
I'm not sure how this would work on Linux. The Wayland folks recently added an extension for blurring window backgrounds and it looks like it can be dynamically added and removed but I can't test that right now and have no idea which compositors have implemented it.
| break; | ||
| case WM_DWMCOMPOSITIONCHANGED: | ||
| if (m_isUnified && (IS_WINVISTA)) { | ||
| if (m_isUnified || m_hasBackdrop) { |
There was a problem hiding this comment.
Should a backdrop automatically set the window margin akin to UNIFIED (and maybe EXTENDED)? In my opinion, this should be left to the stage style for consistency.
Like if I use the normal stage style with a backdrop, I might not want the full backdrop to be visible like that. Maybe I only want the backdrop the be visible in the title bar.
There was a problem hiding this comment.
Good question. I had to do this to make the backdrops work. I tweaked Prism to ensure the swap chain supported an alpha channel but apparently that's not enough. To get D3D9 to alpha composite onto the backdrop we have to make this call to invoke the old Aero glass logic (the effect UNIFIED was originally designed for). I'll keep looking into this but for now when there's a backdrop DECORATED and UNIFIED look the same.
I'll also take a look at DWMSBT_AUTO which states that it only draws the backdrop in the title bar but doesn't give us any control over the effect.
Most Microsoft apps that employ these backdrops extend the backdrop into the title bar though there's a lot of subtle variation (and then there's Weather which looks like it was written by a different company).
|
This will need a CSR. /reviewers 2 |
|
@kevinrushforth |
|
@kevinrushforth has indicated that a compatibility and specification (CSR) request is needed for this pull request. @beldenfox please create a CSR request for issue JDK-8375578 with the correct fix version. This pull request cannot be integrated until the CSR request is approved. |
|
This probably needs 3 reviewers instead of 2. |
|
/reviewers 3 |
|
@kevinrushforth |
|
Depending on how long this takes, would it be possible to extract the fix for the unified stage BackBufferFormat (if it actually fixes all cases) as a separate PR? That would make the review easier to split a feature addition and bugfix into two issues. I can definitely do the testing for the fix |
It would probably be faster for you to test and submit the PR yourself. The fix is just the two lines in native-prism-d3d where the BackBufferFormat is set to D3DFMT_A8R8G8B8 instead of being left unspecified. Based on the write up for bug JDK-8154847 if you don't set the back buffer format what you get varies depending on the graphic card. It should be OK to enable alpha unconditionally, in the end it should only affect UNIFIED. Most stage styles wipe out the alpha channel by drawing an opaque background. The exceptions are TRANSPARENT and UNIFIED and only UNIFIED uses the swap chain. If you'd prefer I can enter the PR but it would probably have to wait until next week. |
|
Can I somehow attribute the PR authorship to you in skara? |
Yes: |
|
@beldenfox you can now rebase on master and remove the BackBufferFormat adjustments in this PR. That should make this PR more straightforward |
|
@beldenfox This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply issue a |
|
This PR has been on hold while we figure out how it would work with the D3D12. For legacy technologies like D3D9 the OS performs some magic; invoking the old Aero glass effect by calling ::DwmExtendFrameIntoClientArea automatically allows the D3D9 swap chain to composite over the backdrop. For D3D12 it looks like we need to implement this directly by building a compositing swap chain. We haven't sorted out exactly how this will work. I would like to enable dynamically changing the backdrop after the window is shown (moving the call from Stage.initBackdrop to Stage.setBackdrop or Scene.setBackdrop). But it remains to be seen whether we can get that to work with D3D12 or not. |
|
@beldenfox This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply issue a |
list of materials is now open-ended.
|
I've updated this PR to treat StageBackdrops more like fonts. A backdrop is now an object that's built by passing a material to a factory method. Materials are specified using strings and the list of materials can vary by platform. All platforms that support backdrops will provide two default materials ("Window" and "Partial") and some number of materials that are platform-specific. For platform-specific materials the naming convention is the same as platform preferences; macOS prefixes the material name with "macOS." and Windows prefixes it with "Windows.". In the future we may want to provide a factory method that accepts material-specific options but at the moment there are none. I added "macOS.Glass" and "macOS.ClearGlass" to the list of available materials on macOS 26 but had to remove them since they require the macOS 26 SDK and the build machines are still on macOS 15. |
|
For Linux/Wayland support, the ext-background-effect-v1 protocol (which is now implemented in KWin, with KDE Plasma 6.7 shipping this support on June 16, 2026, wlroots and Niri with planned support coming to other DE's such as Cosmic and Hyprland as well as others) could be a viable path forward. Unlike some of the platform-specific constraints discussed above, this protocol supports dynamically adding/removing background effects, which aligns with the goal of supporting setBackdrop() after the window is shown. Reference: https://wayland.app/protocols/ext-background-effect-v1 Real-world implementations in Ghostty (ghostty-org/ghostty#10727) and Quickshell (quickshell-mirror/quickshell#566) demonstrate how this works in practice. Additional notes: For testing purposes you can test this on KDE Neon Unstable edition as a Live ISO or in a VM: |
|
@RealCrystalNight At the moment, JavaFX runs on Wayland through XWayland, which prevents it from using Wayland-specific extensions. Native Wayland support would be required for that, but implementing it is a significant effort. |
|
@RealCrystalNight Thank you for the details, I'll take a look at the PR's you linked to. Last time I looked it seemed that the Wayland protocols only handled blurring the content behind the window. On macOS and Win11 the OS starts there and then layers other effects on top of the blur to improve text legibility, handle light and dark mode, and provide feedback on the window's active state. If that's still the case it will take some effort to get Wayland in line with macOS and Win11. My plan is to keep the current Stage.installBackdrop API rather than switch to a dynamic Stage.setBackdrop call. This won't be an issue if we ever want to enable CSS support for specifying the backdrop. For popups like menus changes to the stylesheets are processed before the platform window is created so the desired backdrop is known at window creation time anyway. I don't currently have a use case for Stage.setBackdrop and it does simplify matters to know the backdrop at window creation time and only handle setting it up once. And, yes, I'm working on the CSR. |
|
The proposed API feels very stringly-typed, and I'm not sure if that's a good model. Specifically, I'm looking at future enhancements that expose configuration options which are available on some platforms and which we might want to support. I've played around with your code for a bit and came up with a slightly different idea of how we could design an API. Feel free to criticize or dismiss: // Public API:
public sealed interface StageBackdrop permits DefaultStageBackdrop, PlatformStageBackdrop {
// Default backdrops are available as static constants
StageBackdrop WINDOW = DefaultStageBackdrop.WINDOW;
StageBackdrop PARTIAL = DefaultStageBackdrop.PARTIAL;
/**
* {@return the name of the backdrop}
*/
String name();
// Potential future enhancement:
/**
* {@return a backdrop with the specified option}
*
* @throws NullPointerException if {@code key} or {@code value} is {@code null}
* @throws IllegalArgumentException if the option is not supported by this backdrop
*/
StageBackdrop withOption(String key, Object value);
/**
* {@return all platform backdrops supported on this system}
*
* For systems where backdrops are not supported this will be an empty list.
*/
static List<StageBackdrop> getPlatformBackdrops() {
return PlatformStageBackdrop.BACKDROPS;
}
}
// Platform-independent implementations:
public enum com.sun.javafx.stage.DefaultStageBackdrop implements StageBackdrop {
WINDOW,
PARTIAL
}
// Named platform implementations:
public record com.sun.javafx.stage.PlatformStageBackdrop(String name) implements StageBackdrop {
public static final List<StageBackdrop> BACKDROPS =
Toolkit.getToolkit().getBackdropMaterials().stream()
.map(name -> (StageBackdrop)new PlatformStageBackdrop(name))
.toList();
}
The only way to obtain a Here's how the API could be used: // Default backdrop
stage.initBackdrop(StageBackdrop.WINDOW);
// Platform-specific backdrop with default fallback
stage.initBackdrop(StageBackdrop.getPlatformBackdrops().stream()
.filter(b -> b.name().equals("Windows.Transient"))
.findFirst()
.orElse(StageBackdrop.WINDOW));
// Configurable backdrop with default fallback
stage.initBackdrop(StageBackdrop.getPlatformBackdrops().stream()
.filter(b -> b.name().equals("Windows.Mica"))
.map(b -> b.withOption("TintColor", Color.RED))
.findFirst()
.orElse(StageBackdrop.WINDOW)); |
|
@mstr2 Thank you for looking into this in such detail. I really appreciate it. I've been going back and forth on your proposal over the last few days. I'm now in favor of changing the API to more or less match what you've written up. There's a lot to be said for firmly separating the standard backdrops from the platform-dependent ones and providing different means of accessing them. I'll start migrating the API later this week. |
|
One thing we should probably do is document the platform backdrops that might be available, similar to how we document the platform preferences that are potentially available. Short of letting users choose from a list of backdrops, there is very little developers can do with the backdrop names at runtime if they don't know the potentially available names in advance. |
|
Could that approach potentially support customization? For example liquid glass allows a tint color, etc. Also, would stuff like the window border radius be considered a part of the backdrop? Because that is also available to be changed via native APIs Edit: I see the customization method |
For the clear glass effect on macOS you can change the corner radius and, yes, that would be one of the backdrop options. I wouldn't provide that for any of the other backdrop effects. You can change the corner radius of an NSVisualEffectView's layer but not the corner radius of the NSWindow so the edging visuals don't match. In theory you could use a transparent stage to get rid of the NSWindow visuals and try to draw the edging lines using JavaFX but I'm dubious that you would get good results. In the next round I'll be removing the non-clear glass effect on macOS since there's some graphic artifacts showing up in the corners. I don't think it's designed for use by top-level windows. |
|
How would the API handle cases where a |
|
In the API I'm working on you can query for supported options. The return is a Map<String, Class<?>> containing the name of each option and the class of object it accepts. Personally I favor ignoring options that aren't supported. After all we ignore backdrops and StageStyles if they're not supported. Throwing exceptions introduces a bunch of testing issues and seems excessive. |
|
What if the backdrop effect is technically supported by the platform, but the user has disabled transparency effects or enabled a “reduced transparency” accessibility setting? |
For the standard backdrops (WINDOW and PARTIAL) reduced transparency doesn't matter much. In normal operation the window backdrop will track the window's color scheme (LIGHT or DARK). The translucency effect might go away but the overall background color will be the same. The real complication comes from the other accessibility settings; on Windows the backdrop will also respond to the high contrast themes. The most appropriate way to draw on a standard backdrop is to make no assumptions about what color it's going to display and consult Platform Preferences for appropriate foreground colors. For the platform-specific backdrops anything goes. Most of them will follow the window's color scheme, some will not. The clear glass effect on macOS is always light colored when "reduced transparency" is off and ignores the color scheme. But if "reduced transparency" is on it starts tracking the color scheme. Most of the other effects on macOS track the window's color scheme and platform accessibility settings. |
…m variants commit dee1903 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Wed May 20 06:18:23 2026 -0700 More minor cleanup commit 457c7eb Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Tue May 19 19:33:15 2026 -0700 Cleaning up imports, comments, etc. commit 947a5e3 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Tue May 19 16:16:23 2026 -0700 Disabling macOS 26 Liquid Glass effect commit 9c3d038 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Tue May 19 16:14:19 2026 -0700 Backdrop options are now dynamic and set on the stage. commit 7d61c2b Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Tue May 19 11:15:09 2026 -0700 Added options query and simplified Windows implementation commit c1514f3 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Mon May 18 12:07:40 2026 -0700 Revamped StageBackdrop API, splitting standard and platform backdrops and adding options commit 608cfa6 Merge: 551c9ec 7110a09 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Thu May 14 07:08:44 2026 -0700 Merge branch 'osbackdrop' into osbackdrop_dev commit 551c9ec Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Wed Apr 8 08:51:49 2026 -0700 Cleanup. commit c2e8ec8 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Sat Apr 4 20:18:07 2026 -0700 Factory for construction backdrops commit 4654e40 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Wed Apr 1 16:20:29 2026 -0700 Some tests call Application.createWindow. Added compatibility version. commit cb41716 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Wed Apr 1 12:34:00 2026 -0700 Removing macOS 26 glass backdrops since they require an updated SDK commit 96f5ab2 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Wed Apr 1 12:16:39 2026 -0700 JavaDoc fixes, Ios fixes commit 547027b Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Wed Apr 1 11:43:47 2026 -0700 Windows implementation of material-based backdrops commit 3b1b479 Author: Martin Fox <beldenfox@users.noreply.github.com> Date: Wed Apr 1 09:44:31 2026 -0700 Backdrop materials are now strings and can be platform-specific.
|
/template append |
|
@beldenfox The pull request template has been appended to the pull request body |
| * | ||
| * @return An unmodifiable list of names of the supported platform backdrops. | ||
| */ | ||
| public static List<String> getPlatformBackdropNames() { |
There was a problem hiding this comment.
What's the reason for returning a list of names instead of a list of backdrops? If you returned the latter, there would be no need for a factory method to create a backdrop instance, and no way to pass in a wrong string.
There was a problem hiding this comment.
When prototyping the CSS code and even the sample app the names were all that mattered. The utility functions I wrote to map strings to backdrops always had to deal with invalid names anyway. It also seems that a factory function aligns better with existing JavaFX API's (see Font) and forcing clients to iterate does not. And I realize that it probably doesn't matter here but iteration doesn't scale well. Overall I felt that a factory function was a better fit.
Put another way, we could return a list of backdrops instead of names but I would still want to provide a static function for finding a backdrop by name. But maybe I'm focusing too much on the specific use cases I encountered.
|
I've pushed a new version. StageBackdrops now come in two flavors, the standard backdrops which are referenced using constants (WINDOW and PARTIAL) and the platform-dependent backdrops which are referenced by name (string). The selection of platform backdrops is not final, the current crop are just for demo purposes. I had to remove the macOS liquid glass backdrops since they require a more recent SDK than the build machines support. If you want to try them out you'll need to edit MacWindow.java (look for ClearGlass) and GlassHostView.m (look for setGlassBackdrop). There's now a call to query a backdrop for its supported options. The call for setting an option has been moved to Stage since you can change the value at any time. That call is currently a no-op since the only backdrop that supports options is macOS.ClearGlass and it's not available. Hooking this up isn't rocket science but is a bit tedious so I'm putting it off. |
This PR enables translucent window backdrops for JavaFX stages on macOS and Windows 11. Since we’re reliant on the operating system for these effects (they typically require real-time blurring of the desktop) I needed to flesh out a fairly complete prototype to sort out the API. I will start a discussion about the API on the mailing list.
There’s a crude manual test for trying out the different backdrop materials.
You’ll want to drag the windows around to avoid having them overlap each other since they’re all created in the center of the screen. For windows without title bars you can click anywhere on the background to drag the window except for TRANSPARENT stages on Windows which are a bit tricker to get a hold of; try to click on a text label.
If you create an UNDECORATED stage on Windows the backdrop won’t be translucent initially. This can be corrected by changing the stage’s color scheme. This is an OS bug that I haven’t found a workaround for.
The changes on Windows 11 are minimal since we’re just invoking an OS feature by calling DwmSetWindowAttribute. I did need to make two small changes to the D3D9 Prism code to ensure that the swap chain and back buffer support an alpha channel so JavaFX can composite its content on top of the backdrop. This is the same way the old UNIFIED stage style worked before it became unreliable (see JDK-8154847).
On macOS I moved the GlassHostView so it’s now a permanent part of the NSWindow. For some time the host view has been a remnant left over from an older approach to implementing fullscreen. Now it serves as a common parent for the NSVisualEffectView that provides the backdrop and the GlassView3D that contains the JavaFX content. Making it the permanent contentView of the NSWindow simplifies some code.
To validate the API I did prototype this for Windows 10 (thanks @mstr2!). Well, I prototyped this using DirectComposition so it should work on Win10 but I can't test Win10 myself. Using DirectComposition is much more involved so I shelved that implementation for now but it does inform the API. It’s the reason the backdrop needs to be specified before the Java window is shown and the platform window created.
Progress
Issue
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/2048/head:pull/2048$ git checkout pull/2048Update a local copy of the PR:
$ git checkout pull/2048$ git pull https://git.openjdk.org/jfx.git pull/2048/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 2048View PR using the GUI difftool:
$ git pr show -t 2048Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/2048.diff
Using Webrev
Link to Webrev Comment