On Wed, 8 Apr 2026 17:58:25 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.
>
> Martin Fox has updated the pull request with a new target base due to a merge 
> or a rebase. The pull request now contains eight commits:
> 
>  - Merge remote-tracking branch 'upstream/master' into osbackdrop
>  - Backdrops are now objects created by specifying a material. The
>    list of materials is now open-ended.
>  - Merge remote-tracking branch 'upstream/master' into osbackdrop
>  - Merge remote-tracking branch 'upstream/master' into osbackdrop
>  - Merge remote-tracking branch 'upstream/master' into osbackdrop
>  - Removed unused import
>  - Merge remote-tracking branch 'upstream/master' into osbackdrop
>  - OS supplied translucent window backdrops

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](https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/ui/apply-mica-win32)
 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();
}


`StageBackdrop.getPlatformBackdrops()` would only return the platform-prefixed 
named backdrops, not the two platform-independent variants. Users would only 
interact with `StageBackdrop` objects, not with strings. This also gets rid of 
the "material" terminology, which I find a bit confusing (I ask the backdrop 
for a list of materials, and then turn a material back into a backdrop?).

The only way to obtain a `StageBackdrop` is to use a static default instance, 
choose one of the platform-provided backdrops, or potentially in the future 
start with a platform-provided backdrop, and derive a new variant with options 
from it. Backdrop objects are always immutable.

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));

-------------

PR Comment: https://git.openjdk.org/jfx/pull/2048#issuecomment-4400561734

Reply via email to