On Fri, 18 Sep 2026 19:55:06 GMT, Martin Fox <[email protected]> wrote:
>> 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. >> >> java @build/run.args -Djavafx.enablePreview=true >> tests/manual/stage/BackdropTest.java >> >> 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](https://bugs.openjdk.org/browse/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. >> >> --------- >> - [x] I confirm that I make this contribution in accordance with the >> [OpenJDK Interim AI Policy](https://openjdk.org/legal/ai). > > Martin Fox has updated the pull request incrementally with one additional > commit since the last revision: > > Fixed some bugs in how window flags are set up. modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java line 807: > 805: } > 806: > 807: public boolean emulateBackdrop() { `emulateBackdrop` makes it sound like the method emulates a backdrop. Maybe something along the lines of `isEmulatedBackdrop` could make the meaning clearer. modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java line 253: > 251: } > 252: > 253: private static Map<String, Integer> backdropStyles = null; You could initialize the style map directly, which would remove the need to call `initBackdropStyles()` in several places: Suggestion: private static Map<String, Integer> backdropStyles = Map.ofEntries( Map.entry("macOS.HUD", BackdropID.HUD), Map.entry("macOS.Menu", BackdropID.MENU), Map.entry("macOS.Popover", BackdropID.POPOVER), Map.entry("macOS.Tooltip", BackdropID.TOOLTIP) ); modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java line 279: > 277: public static List<String> getPlatformBackdropStyleNames() { > 278: initBackdropStyles(); > 279: return Collections.unmodifiableList(new > ArrayList<>(backdropStyles.keySet())); While this works, I believe it technically relies on implementation details. The `HashMap` class documentation states that no guarantee regarding the order of its mappings is given; in particular, the documentation states that the order may change _over time_ (whatever this means). Callers of `StageBackdropStyle.getPlatformStyleNames()` end up here, and they're given a new list for every call. When called and indexed in a loop, this could _very hypothetically_ lead to incorrect indexing of the list elements. The cleanest solution is probably to give callers the same list instance every time. modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java line 320: > 318: @Override > 319: public void setBackdropOption(String name, Object option) { > 320: if (name == "TintColor") { These strings come from application code, so we're not guaranteed to receive interned strings here. Use `equals` here and in other places to be on the safe side. modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java line 467: > 465: var preferences = Platform.getPreferences(); > 466: var highcontrast = preferences.get("Windows.SPI.HighContrast"); > 467: if (highcontrast instanceof Boolean hc) { Suggestion: return highcontrast == Boolean.TRUE; modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PlatformStageBackdropStyle.java line 36: > 34: */ > 35: public final class PlatformStageBackdropStyle implements > StageBackdropStyle { > 36: public String name; Here and in all new files: we usually use an empty line after the opening brace. modules/javafx.graphics/src/main/java/com/sun/javafx/stage/StageBackdropHelper.java line 36: > 34: */ > 35: public class StageBackdropHelper { > 36: private static final StageBackdropHelper theInstance; 1. You can remove this field, it's not used. 2. The class should be final and have a private constructor, as the simple private accessor pattern doesn't permit polymorphic helpers/accessors (that would need to be implemented differently). modules/javafx.graphics/src/main/java/com/sun/javafx/stage/StageBackdropHelper.java line 53: > 51: public static void setStageBackdropAccessor(final > StageBackdropAccessor newAccessor) { > 52: if (stageBackdropAccessor != null) { > 53: throw new IllegalStateException(); That's not an illegal state (a private-access helper has no state), it's a violated invariant. Most private-access helper don't bother checking things like this. modules/javafx.graphics/src/main/java/javafx/stage/StageBackdropStyle.java line 119: > 117: * backdrop styles > 118: */ > 119: public static List<String> getPlatformStyleNames() { I'd start with minimal API first, then only add more if there's an established use case. So `getPlatformStyleNames` and `getStandardStyles` would be on my chopping block, as these are easily reproducible by application developers. modules/javafx.graphics/src/main/java/javafx/stage/StageBackdropStyle.java line 140: > 138: > 139: /** > 140: * Creates an optional platform backdrop style for the specified > name. I don't think we need to guarantee `Creates`. `Gets` is sufficient. modules/javafx.graphics/src/main/java/javafx/stage/StageBackdropStyle.java line 145: > 143: * @return the optional backdrop style > 144: */ > 145: public static Optional<StageBackdropStyle> style(String name) { Some alternatives for consideration: `ofNamed`, `ofPlatformName` ------------- PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160566123 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160286795 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160793153 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160479719 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160527931 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160428473 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160376264 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160416354 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160634907 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160722928 PR Review Comment: https://git.openjdk.org/jfx/pull/2048#discussion_r4160660889
