From 8ad3e0d53beadce741ca2fd4c7c86f2498c2e740 Mon Sep 17 00:00:00 2001 From: Martin Fox Date: Wed, 21 Jan 2026 09:24:23 -0800 Subject: [PATCH 1/4] OS supplied translucent window backdrops --- .../java/com/sun/javafx/PreviewFeature.java | 3 +- .../java/com/sun/glass/ui/Application.java | 9 + .../main/java/com/sun/glass/ui/Window.java | 18 + .../com/sun/glass/ui/mac/MacApplication.java | 5 + .../com/sun/glass/ui/win/WinApplication.java | 2 + .../java/com/sun/javafx/tk/DummyToolkit.java | 4 +- .../main/java/com/sun/javafx/tk/Toolkit.java | 4 +- .../com/sun/javafx/tk/quantum/GlassScene.java | 2 +- .../sun/javafx/tk/quantum/QuantumToolkit.java | 10 +- .../sun/javafx/tk/quantum/WindowStage.java | 23 +- .../application/ConditionalFeature.java | 10 + .../src/main/java/javafx/stage/Stage.java | 37 +- .../main/java/javafx/stage/StageBackdrop.java | 71 ++++ .../src/main/native-glass/mac/GlassHostView.h | 9 +- .../src/main/native-glass/mac/GlassHostView.m | 39 ++- .../src/main/native-glass/mac/GlassView.m | 19 +- .../main/native-glass/mac/GlassViewDelegate.h | 1 - .../main/native-glass/mac/GlassViewDelegate.m | 19 -- .../src/main/native-glass/mac/GlassWindow.h | 2 + .../src/main/native-glass/mac/GlassWindow.m | 39 ++- .../src/main/native-glass/win/BaseWnd.cpp | 27 ++ .../src/main/native-glass/win/BaseWnd.h | 1 + .../native-glass/win/GlassApplication.cpp | 12 + .../src/main/native-glass/win/GlassWindow.cpp | 48 ++- .../src/main/native-glass/win/GlassWindow.h | 9 + .../main/native-prism-d3d/D3DContextInit.cc | 1 + .../native-prism-d3d/D3DResourceManager.cc | 1 + .../com/sun/javafx/pgstub/StubToolkit.java | 4 +- tests/manual/stage/BackdropTest.java | 320 ++++++++++++++++++ 29 files changed, 664 insertions(+), 85 deletions(-) create mode 100644 modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java create mode 100644 tests/manual/stage/BackdropTest.java diff --git a/modules/javafx.base/src/main/java/com/sun/javafx/PreviewFeature.java b/modules/javafx.base/src/main/java/com/sun/javafx/PreviewFeature.java index 482ef6d23b5..29f8ce4e090 100644 --- a/modules/javafx.base/src/main/java/com/sun/javafx/PreviewFeature.java +++ b/modules/javafx.base/src/main/java/com/sun/javafx/PreviewFeature.java @@ -39,7 +39,8 @@ public enum PreviewFeature { // Add preview feature constants here: // TEST_FEATURE("Test Feature") STAGE_STYLE_EXTENDED("StageStyle.EXTENDED"), - HEADER_BAR("HeaderBar"); + HEADER_BAR("HeaderBar"), + WINDOW_BACKDROP("WindowBackdrop"); PreviewFeature(String featureName) { this.featureName = featureName; diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java index 8029cd75d7a..5eb51322336 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java @@ -717,6 +717,15 @@ public final boolean supportsExtendedWindows() { return _supportsExtendedWindows(); } + protected boolean _supportsWindowBackdrops() { + return false; + } + + public final boolean supportsWindowBackdrops() { + checkEventThread(); + return _supportsWindowBackdrops(); + } + protected boolean _supportsSystemMenu() { // Overridden in subclasses return false; diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java index e11cee2d21b..9176ce62b9e 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java @@ -178,6 +178,14 @@ static protected void remove(Window window) { */ @Native public static final int DARK_FRAME = 1 << 11; + /** + * The backdrop styles take up two bits. 0 is no backdrop. + */ + @Native public static final int BACKDROP_MASK = 3 << 12; + @Native public static final int WINDOW_BACKDROP = 1 << 12; + @Native public static final int TABBED_BACKDROP = 2 << 12; + @Native public static final int TRANSIENT_BACKDROP = 3 << 12; + final static public class State { @Native public static final int NORMAL = 1; @Native public static final int MINIMIZED = 2; @@ -281,6 +289,11 @@ protected Window(Window owner, Screen screen, int styleMask) { styleMask &= ~TRANSPARENT; } + if (((styleMask & BACKDROP_MASK) != 0) + && !Application.GetApplication().supportsWindowBackdrops()) { + styleMask &= ~BACKDROP_MASK; + } + this.owner = owner; this.styleMask = styleMask; this.isDecorated = (this.styleMask & Window.TITLED) != 0; @@ -750,6 +763,11 @@ public boolean isFocused() { return this.isFocused; } + public boolean hasBackdrop() { + // The backdrop flags are only set if backdrops are supported. + return (this.styleMask & Window.BACKDROP_MASK) != 0; + } + protected abstract boolean _requestFocus(long ptr, int event); /** * Requests or resigns focus on this window. diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java index b2813abcf9c..e3b7466de4f 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java @@ -402,6 +402,11 @@ protected boolean _supportsExtendedWindows() { return true; } + @Override + protected boolean _supportsWindowBackdrops() { + return true; + } + @Override native protected boolean _supportsSystemMenu(); // NOTE: this will not return a valid result until the native _runloop diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java index 68e401d2181..2e2ec005609 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java @@ -341,6 +341,8 @@ protected boolean _supportsExtendedWindows() { return true; } + @Override native protected boolean _supportsWindowBackdrops(); + @Override public String getDataDirectory() { checkEventThread(); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java index 06f91d51ffc..0d141c42d4f 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java @@ -44,6 +44,7 @@ import javafx.stage.FileChooser.ExtensionFilter; import javafx.stage.Modality; import javafx.stage.StageStyle; +import javafx.stage.StageBackdrop; import javafx.stage.Window; import java.io.File; import java.io.InputStream; @@ -102,7 +103,8 @@ public void exitAllNestedEventLoops() { @Override public TKStage createTKStage(Window peerWindow, StageStyle stageStyle, boolean primary, - Modality modality, TKStage owner, boolean rtl, boolean darkFrame) { + Modality modality, TKStage owner, boolean rtl, boolean darkFrame, + StageBackdrop backdrop) { throw new UnsupportedOperationException("Not supported yet."); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java index b248b0c334f..6606647c226 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java @@ -51,6 +51,7 @@ import javafx.stage.FileChooser.ExtensionFilter; import javafx.stage.Modality; import javafx.stage.StageStyle; +import javafx.stage.StageBackdrop; import javafx.stage.Window; import java.io.File; import java.io.InputStream; @@ -361,7 +362,8 @@ protected Toolkit() { public abstract boolean isNestedLoopRunning(); public abstract TKStage createTKStage(Window peerWindow, StageStyle stageStyle, boolean primary, - Modality modality, TKStage owner, boolean rtl, boolean darkFrame); + Modality modality, TKStage owner, boolean rtl, boolean darkFrame, + StageBackdrop backdrop); public abstract TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner); public abstract TKStage createTKEmbeddedStage(HostInterface host); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java index ed5cc2a2f9b..c0258b3f447 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java @@ -317,7 +317,7 @@ protected Color getClearColor() { return Color.WHITE; } else if (fillPaint.isOpaque() || (windowStage != null && windowStage.getPlatformWindow() != null && - windowStage.getPlatformWindow().isUnifiedWindow())) { + windowStage.allowsTransparentFill())) { //For bare windows the transparent fill is allowed if (fillPaint.getType() == Paint.Type.COLOR) { return (Color)fillPaint; diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java index ed524fb1d92..d6760236d59 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java @@ -54,6 +54,7 @@ import javafx.stage.FileChooser; import javafx.stage.Modality; import javafx.stage.StageStyle; +import javafx.stage.StageBackdrop; import javafx.stage.Window; import java.io.File; import java.io.InputStream; @@ -614,9 +615,10 @@ void vsyncHint() { } @Override public TKStage createTKStage(Window peerWindow, StageStyle stageStyle, boolean primary, - Modality modality, TKStage owner, boolean rtl, boolean darkFrame) { + Modality modality, TKStage owner, boolean rtl, boolean darkFrame, + StageBackdrop backdrop) { assertToolkitRunning(); - WindowStage stage = new WindowStage(peerWindow, stageStyle, modality, owner, darkFrame); + WindowStage stage = new WindowStage(peerWindow, stageStyle, modality, owner, darkFrame, backdrop); if (primary) { stage.setIsPrimary(); } @@ -699,7 +701,7 @@ private boolean maxNestedEventLoopsHit() { @Override public TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner) { assertToolkitRunning(); - WindowStage stage = new WindowStage(peerWindow, popupStyle, null, owner, false); + WindowStage stage = new WindowStage(peerWindow, popupStyle, null, owner, false, StageBackdrop.DEFAULT); stage.setIsPopup(); stage.init(systemMenu); return stage; @@ -1265,6 +1267,8 @@ public boolean isSupported(ConditionalFeature feature) { return Application.GetApplication().supportsUnifiedWindows(); case EXTENDED_WINDOW: return Application.GetApplication().supportsExtendedWindows(); + case WINDOW_BACKDROP: + return Application.GetApplication().supportsWindowBackdrops(); case TWO_LEVEL_FOCUS: return Application.GetApplication().hasTwoLevelFocus(); case VIRTUAL_KEYBOARD: diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java index 7c3ccdc9b10..c54810f8f5a 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java @@ -35,6 +35,7 @@ import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; +import javafx.stage.StageBackdrop; import com.sun.glass.events.WindowEvent; import com.sun.glass.ui.*; @@ -62,6 +63,7 @@ public class WindowStage extends GlassStage { private StageStyle style; private GlassStage owner = null; private Modality modality = Modality.NONE; + private StageBackdrop backdrop = StageBackdrop.DEFAULT; private OverlayWarning warning = null; private boolean rtl = false; @@ -87,11 +89,12 @@ public class WindowStage extends GlassStage { ".QuantumMessagesBundle", LOCALE); public WindowStage(javafx.stage.Window peerWindow, final StageStyle stageStyle, Modality modality, - TKStage owner, boolean darkFrame) { + TKStage owner, boolean darkFrame, final StageBackdrop backdrop) { this.style = stageStyle; this.owner = (GlassStage)owner; this.modality = modality; this.darkFrame = darkFrame; + this.backdrop = backdrop; if (peerWindow instanceof javafx.stage.Stage) { fxStage = (Stage)peerWindow; @@ -183,6 +186,20 @@ private void initPlatformWindow() { windowMask |= Window.DARK_FRAME; } + switch (backdrop) { + case DEFAULT: + break; + case WINDOW: + windowMask |= Window.WINDOW_BACKDROP; + break; + case TABBED: + windowMask |= Window.TABBED_BACKDROP; + break; + case TRANSIENT: + windowMask |= Window.TRANSIENT_BACKDROP; + break; + } + platformWindow = app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask); platformWindow.setResizable(resizable); platformWindow.setFocusable(focusable); @@ -919,4 +936,8 @@ public void setDarkFrame(boolean value) { platformWindow.setDarkFrame(value); } } + + public boolean allowsTransparentFill() { + return transparent || platformWindow.isUnifiedWindow() || platformWindow.hasBackdrop(); + } } diff --git a/modules/javafx.graphics/src/main/java/javafx/application/ConditionalFeature.java b/modules/javafx.graphics/src/main/java/javafx/application/ConditionalFeature.java index 260f21928c8..d631a122962 100644 --- a/modules/javafx.graphics/src/main/java/javafx/application/ConditionalFeature.java +++ b/modules/javafx.graphics/src/main/java/javafx/application/ConditionalFeature.java @@ -163,6 +163,16 @@ public enum ConditionalFeature { @Deprecated(since = "25") EXTENDED_WINDOW, + /** + * Indicates that a system supports the use of a {@code StageBackdrop}. + *

+ * This feature is currently supported on Windows 11 and macOS. + * @since 27 + * @deprecated This is a preview feature which may be changed or removed in a future release. + */ + @Deprecated(since = "27") + WINDOW_BACKDROP, + /** * Indicates whether or not controls should use two-level focus. Two-level * focus is when separate operations are needed in some controls to first diff --git a/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java b/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java index f3016ebd5ea..3fb01406526 100644 --- a/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java +++ b/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java @@ -566,6 +566,39 @@ public final Window getOwner() { return owner; } + private StageBackdrop backdrop = StageBackdrop.DEFAULT; + + /** + * Specifies the backdrop for this stage. This must be done prior to + * making the stage visible. + * + * @param backdrop the backdrop for this stage. + * + * @throws IllegalStateException if this property is set after the stage + * has ever been made visible. + * + * @defaultValue StageBackdrop.DEFAULT + */ + @SuppressWarnings("deprecation") + public final void initBackdrop(StageBackdrop backdrop) { + PreviewFeature.WINDOW_BACKDROP.checkEnabled(); + + if (hasBeenVisible) { + throw new IllegalStateException("Cannot set backdrop once stage has been set visible"); + } + + this.backdrop = backdrop; + } + + /** + * Retrieves the backdrop for this stage. + * + * @return the backdrop. + */ + public final StageBackdrop getBackdrop() { + return backdrop; + } + /** * Specifies whether this {@code Stage} should be a full-screen, * undecorated window. @@ -1116,10 +1149,10 @@ private void doVisibleChanging(boolean value) { ColorScheme colorScheme = scene != null ? scene.getPreferences().getColorScheme() : PlatformImpl.getPlatformPreferences().getColorScheme(); - StageStyle stageStyle = getStyle(); setPeer(toolkit.createTKStage(this, stageStyle, isPrimary(), - getModality(), tkStage, rtl, colorScheme == ColorScheme.DARK)); + getModality(), tkStage, rtl, colorScheme == ColorScheme.DARK, + this.backdrop)); getPeer().setMinimumSize((int) Math.ceil(getMinWidth()), (int) Math.ceil(getMinHeight())); getPeer().setMaximumSize((int) Math.floor(getMaxWidth()), diff --git a/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java b/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java new file mode 100644 index 00000000000..3df27eb542b --- /dev/null +++ b/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javafx.stage; + +import javafx.application.ConditionalFeature; +import javafx.application.Platform; + +/** + * This enum defines the possible backdrops for a {@code Stage}. Backdrops are + * typically drawn by the operating system and appear behind the Scene's fill + * and background. The specific effects vary but in general the backdrop will + * track the window's color scheme. + * + * Backdrops are a conditional feature. Currently they are only supported + * on Windows 11 and macOS. + * + * @since 27 + */ +@Deprecated(since = "27") +public enum StageBackdrop { + + /** + * The default backdrop consistent with earlier versions of JavaFX. + */ + DEFAULT, + + /** + * A backdrop to be used when the backdrop is visible across most of the + * window. This is opaque enough that you can draw text on it using the + * platform's foreground color. + */ + WINDOW, + + /** + * A backdrop to be used when the backdrop is visible along one edge of + * the window, like behind a sidebar or tab bar. This is opaque enough + * that you can draw text on it using the platform's foreground color. + */ + TABBED, + + /** + * A backdrop useful for transient windows or heads-up displays. On some + * platform this backdrop is so translucent that you cannot reliably draw + * text on it. It's recommended that you set the Scene's fill to a + * non-opaque color to overlay on this backdrop. + */ + TRANSIENT +} diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h index d11fef9d7da..eba0d247958 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h @@ -26,11 +26,12 @@ #import #import -// host view to which our views attach, so we can move our view in/out @interface GlassHostView : NSView { -@public - NSView *view; +@private + NSView *jfxView; + NSView *backdropView; } - +-(void)setJFXView:(NSView*)view; +-(void)setBackdrop:(NSVisualEffectMaterial)material; @end diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m index e18fd841dfe..4b9f469a3d7 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m @@ -26,6 +26,30 @@ #import "GlassHostView.h" @implementation GlassHostView : NSView +- (void)setJFXView:(NSView*)view +{ + if (jfxView != nil) { + [jfxView removeFromSuperview]; + } + jfxView = view; + if (jfxView) { + [self addSubview: jfxView positioned: NSWindowAbove relativeTo: backdropView]; + } +} + +// Called when the window is first created. +-(void)setBackdrop:(NSVisualEffectMaterial)material +{ + NSVisualEffectView* effect = [[NSVisualEffectView alloc] initWithFrame: self.bounds]; + effect.material = material; + [self addSubview: effect]; +} + +- (void)resizeSubviewsWithOldSize:(NSSize) oldSize { + for (NSView* child in self.subviews) { + child.frame = self.bounds; + } +} - (void)dealloc { @@ -72,21 +96,6 @@ - (BOOL)mouseDownCanMoveWindow return NO; } -- (void)addSubview:(NSView *)aView -{ - [super addSubview:aView]; - self->view = aView; -} - -- (void)drawRect:(NSRect)rect -{ - //NSLog(@"Sparkle: drawRect: [%@] %.2f,%.2f %.2fx%.2f,", self, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); - //NSLog(@"Sparkle: frame: [%@] %.2f,%.2f %.2fx%.2f,", self, [self frame].origin.x, [self frame].origin.y, [self frame].size.width, [self frame].size.height); - //NSLog(@"Sparkle: bounds: [%@] %.2f,%.2f %.2fx%.2f,", self, [self bounds].origin.x, [self bounds].origin.y, [self bounds].size.width, [self bounds].size.height); - //[[NSColor redColor] set]; - //NSRectFill([self bounds]); -} - - (BOOL) isFlipped { return YES; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassView.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassView.m index 11374f7b7a6..fbc8dd7f492 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassView.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassView.m @@ -288,17 +288,8 @@ jCapabilitiesRef = (*env)->NewGlobalRef(env, jCapabilities); } - // embed ourselves into GlassHostView, so we can later swap our view between windows (ex. fullscreen mode) - NSView *hostView = [[GlassHostView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]; // alloc creates ref count of 1 - [hostView setAutoresizingMask:(NSViewWidthSizable|NSViewHeightSizable)]; - [hostView setAutoresizesSubviews:YES]; + NSView* view = [[GlassView3D alloc] initWithFrame:NSMakeRect(0, 0, 10, 10) withJview:jView withJproperties:jCapabilities]; - //NSLog(@"--- hostView bounds = (%f, %f) - (%f, %f)", [hostView bounds].origin.x, [hostView bounds].origin.y, [hostView bounds].size.width, [hostView bounds].size.height); - - NSView* view = [[GlassView3D alloc] initWithFrame:[hostView bounds] withJview:jView withJproperties:jCapabilities]; - [view setAutoresizingMask:(NSViewWidthSizable|NSViewHeightSizable)]; - - [hostView addSubview:view]; jfieldID jfID = (*env)->GetFieldID(env, jViewClass, "ptr", "J"); GLASS_CHECK_EXCEPTION(env); (*env)->SetLongField(env, jView, jfID, ptr_to_jlong(view)); @@ -441,10 +432,10 @@ GLASS_POOL_ENTER; { NSView *view = getGlassView(env, jPtr); - NSView * host = [view superview]; - if (host != nil) { - [view removeFromSuperview]; - [host release]; + NSView *superView = [view superview]; + if ([superView isKindOfClass: [GlassHostView class]]) { + GlassHostView* host = (GlassHostView*) superView; + [host setJFXView: nil]; } [view release]; } diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.h b/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.h index fbb5cd9afd3..22d0d7944bb 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.h @@ -44,7 +44,6 @@ typedef enum GestureMaskType { NSTrackingRectTag trackingRect; - GlassHostView *parentHost; NSWindow *parentWindow; CGFloat parentWindowAlpha; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m index 955b02077cf..1270c51d7cf 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m @@ -183,9 +183,6 @@ - (void)dealloc [self->lastEvent release]; self->lastEvent = nil; - [self->parentHost release]; - self->parentHost = nil; - [self->parentWindow release]; self->parentWindow = nil; @@ -216,10 +213,6 @@ - (void)viewDidMoveToWindow GET_MAIN_JENV; if ([self->nsView window] != nil) { - if (self->parentHost == nil) - { - self->parentHost = (GlassHostView*)[[self->nsView superview] retain]; - } if (self->parentWindow == nil) { self->parentWindow = [[self->nsView window] retain]; @@ -1248,18 +1241,6 @@ - (void)setResizableForFullscreen:(BOOL)resizable [window setStyleMask: mask]; } -/* - The hierarchy for our view is view -> superview (host) -> window - - 1. create superview (new host) for our view - 2. create fullscreen window with the new superview - 3. create the background window (for fading out the desktop) - 4. remove our view from the window superview and insert it into the fullscreen window superview - 5. show our fullscreen window (and hide the original window) - 6. attach to it our background window (which will show it as well) - 7. zoom out our fullscreen window and at the same time animate the background window transparency - 8. enter fullscreen - */ - (void)enterFullscreenWithAnimate:(BOOL)animate withKeepRatio:(BOOL)keepRatio withHideCursor:(BOOL)hideCursor { LOG("GlassViewDelegate enterFullscreenWithAnimate:%d withKeepRatio:%d withHideCursor:%d", animate, keepRatio, hideCursor); diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.h b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.h index daf2a5de62e..4e6e7324278 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.h @@ -44,6 +44,7 @@ GlassWindow *owner; NSMutableArray *childWindows; + GlassHostView *hostView; GlassView3D *view; NSScreen *currentScreen; GlassMenubar *menubar; @@ -84,6 +85,7 @@ - (void) setMoveToActiveSpaceChildWindows:(BOOL)moveToActiveSpace; // NSWindow overrides delegate methods +- (void)setJFXView:(GlassView3D*)view; - (void)close; - (void)sendEvent:(NSEvent *)event; - (BOOL)canBecomeMainWindow; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m index 2fa282c6358..f55eb840fbf 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m @@ -445,7 +445,11 @@ - (NSColor*)setBackgroundColor:(NSColor *)color } } - +- (void)setJFXView:(GlassView3D*)newView +{ + [hostView setJFXView: newView]; + view = newView; +} @end #pragma mark --- Dispatcher @@ -591,6 +595,21 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr [window->nsWindow setOpaque:YES]; } + window->hostView = [[GlassHostView alloc] initWithFrame: NSMakeRect(0, 0, 0, 0)]; + window->nsWindow.contentView = window->hostView; + + switch (jStyleMask & com_sun_glass_ui_Window_BACKDROP_MASK) { + case com_sun_glass_ui_Window_WINDOW_BACKDROP: + [window->hostView setBackdrop: NSVisualEffectMaterialUnderWindowBackground]; + break; + case com_sun_glass_ui_Window_TABBED_BACKDROP: + [window->hostView setBackdrop: NSVisualEffectMaterialSidebar]; + break; + case com_sun_glass_ui_Window_TRANSIENT_BACKDROP: + [window->hostView setBackdrop: NSVisualEffectMaterialHUDWindow]; + break; + } + window->isDecorated = isTitled || isExtended; window->isExtended = isExtended; window->isTransparent = isTransparent; @@ -916,6 +935,7 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr } NSView *oldView = window->view; + [window setJFXView: getMacView(env, jview)]; window->view = getMacView(env, jview); //NSLog(@" window: %@", window); //NSLog(@" frame: %.2f,%.2f %.2fx%.2f", [window frame].origin.x, [window frame].origin.y, [window frame].size.width, [window frame].size.height); @@ -936,15 +956,6 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr if (window->view != nil) { - CALayer *layer = [window->view getLayer]; - LOG(" layer: %p", layer); - // TODO : Move below logic to CGL specific View/Layer class - if (([layer.sublayers[0] isKindOfClass:[CAOpenGLLayer class]] == YES) && - (([window->nsWindow styleMask] & NSWindowStyleMaskTexturedBackground) == NO)) - { - [((CAOpenGLLayer*)layer) setOpaque:[window->nsWindow isOpaque]]; - } - window->suppressWindowMoveEvent = YES; // JDK-8111165 { NSRect viewFrame = [window->view frame]; @@ -956,19 +967,11 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr [window _setWindowFrameWithRect:NSMakeRect(windowFrame.origin.x, windowFrame.origin.y, windowFrame.size.width, windowFrame.size.height) withDisplay:JNI_TRUE withAnimate:JNI_FALSE]; } - [window->nsWindow setContentView:[window->view superview]]; // use our superview not ourselves! [window->nsWindow setInitialFirstResponder:window->view]; [window->nsWindow makeFirstResponder:window->view]; } window->suppressWindowMoveEvent = NO; } - else - { - // If the contentView is set to nil within performKeyEquivalent: the OS will crash. - NSView* dummy = [[NSView alloc] initWithFrame: NSMakeRect(0, 0, 10, 10)]; - [window->nsWindow performSelectorOnMainThread:@selector(setContentView:) withObject:dummy waitUntilDone:YES]; - [dummy release]; - } } GLASS_POOL_EXIT; GLASS_CHECK_EXCEPTION(env); diff --git a/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.cpp b/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.cpp index d0b52de7dd9..1a5ba7b4541 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.cpp @@ -147,6 +147,33 @@ BOOL BaseWnd::GetDefaultWindowBounds(LPRECT r) return res; } +/*static*/ +BOOL BaseWnd::BackdropsSupported() +{ + HINSTANCE hInst = ::GetModuleHandle(NULL); + TCHAR* szClassName = L"GLASSDEFAULTWINDOW"; + BOOL supported = FALSE; + + WNDCLASS wndcls; + ::ZeroMemory(&wndcls, sizeof(WNDCLASS)); + wndcls.lpfnWndProc = StaticWindowProc; + wndcls.hInstance = hInst; + wndcls.lpszClassName = szClassName; + ::RegisterClass(&wndcls); + + HWND hwnd = ::CreateWindow(szClassName, L"", WS_OVERLAPPED, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, 0, 0); + + DWM_SYSTEMBACKDROP_TYPE type = DWMSBT_AUTO; + supported = SUCCEEDED(DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &type, sizeof(type))); + + ::DestroyWindow(hwnd); + ::UnregisterClass(szClassName, hInst); + + return supported; +} + /*static*/ LRESULT CALLBACK BaseWnd::StaticWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { diff --git a/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.h b/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.h index e9e875d126a..79f42ffe463 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.h +++ b/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.h @@ -50,6 +50,7 @@ class BaseWnd { // returns its bounds. This method is used to find the default window // size/location when CW_USEDEFAULT can't be used (e.g. for WS_POPUP windows). static BOOL GetDefaultWindowBounds(LPRECT r); + static BOOL BackdropsSupported(); HWND GetHWND() { return m_hWnd; } diff --git a/modules/javafx.graphics/src/main/native-glass/win/GlassApplication.cpp b/modules/javafx.graphics/src/main/native-glass/win/GlassApplication.cpp index 880bb50b4d4..56819cad99d 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassApplication.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassApplication.cpp @@ -506,6 +506,18 @@ JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_win_WinApplication__1supportsUn return (IS_WINVISTA); } +/* + * Class: com_sun_glass_ui_Application + * Method: _supportsWindowBackdrops + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_win_WinApplication__1supportsWindowBackdrops + (JNIEnv * env, jobject japplication) +{ + static bool supported = BaseWnd::BackdropsSupported(); + return supported; +} + /* * Class: com_sun_glass_ui_Application * Method: staticScreen_getScreens diff --git a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp index 622e1f2a456..ddc10ffddae 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp @@ -72,11 +72,12 @@ GlassWindow::GlassWindow(jobject jrefThis, bool isTransparent, bool isDecorated, m_isFocusable(true), m_isFocused(false), m_focusEvent(0), - m_isResizable(true), m_isTransparent(isTransparent), m_isDecorated(isDecorated), m_isUnified(isUnified), m_isExtended(isExtended), + m_isResizable(true), + m_hasBackdrop(false), m_hMenu(NULL), m_alpha(255), m_isEnabled(true), @@ -336,7 +337,7 @@ LRESULT GlassWindow::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam) } break; case WM_DWMCOMPOSITIONCHANGED: - if (m_isUnified && (IS_WINVISTA)) { + if (m_isUnified || m_hasBackdrop) { BOOL bEnabled = FALSE; if(SUCCEEDED(::DwmIsCompositionEnabled(&bEnabled)) && bEnabled) { MARGINS dwmMargins = { -1, -1, -1, -1 }; @@ -1332,6 +1333,31 @@ void GlassWindow::SetDarkFrame(bool dark) } } +void GlassWindow::EnableBackdrop(BackdropStyle style) +{ + m_hasBackdrop = true; + + DWM_SYSTEMBACKDROP_TYPE type = DWMSBT_TRANSIENTWINDOW; + switch (style) { + case Window: + type = DWMSBT_MAINWINDOW; + break; + case Tabbed: + type = DWMSBT_TABBEDWINDOW; + break; + case Transient: + type = DWMSBT_TRANSIENTWINDOW; + break; + } + DwmSetWindowAttribute(GetHWND(), DWMWA_SYSTEMBACKDROP_TYPE, &type, sizeof(type)); + + // In case the user asks for the accent color to tint the title bar. We + // don't want DWM to draw this since it doesn't know the correct title + // bar height. + COLORREF captionColor = DWMWA_COLOR_NONE; + DwmSetWindowAttribute(GetHWND(), DWMWA_CAPTION_COLOR, &captionColor, sizeof(captionColor)); +} + void GlassWindow::ShowSystemMenu(int x, int y) { WINDOWPLACEMENT placement; @@ -1468,7 +1494,7 @@ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_win_WinWindow__1createWindow closeable = (mask & com_sun_glass_ui_Window_CLOSABLE) != 0; if (mask & com_sun_glass_ui_Window_EXTENDED) { - mask |= com_sun_glass_ui_Window_TITLED; + dwExStyle = WS_EX_WINDOWEDGE; } if (mask & com_sun_glass_ui_Window_TITLED) { @@ -1508,6 +1534,10 @@ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_win_WinWindow__1createWindow dwExStyle |= WS_EX_NOINHERITLAYOUT | WS_EX_LAYOUTRTL; } + if (mask & com_sun_glass_ui_Window_EXTENDED) { + mask |= com_sun_glass_ui_Window_TITLED; + } + GlassWindow *pWindow = new GlassWindow(jThis, (mask & com_sun_glass_ui_Window_TRANSPARENT) != 0, @@ -1532,6 +1562,18 @@ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_win_WinWindow__1createWindow if (mask & com_sun_glass_ui_Window_DARK_FRAME) { pWindow->SetDarkFrame(true); } + + switch (mask & com_sun_glass_ui_Window_BACKDROP_MASK) { + case com_sun_glass_ui_Window_WINDOW_BACKDROP: + pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Window); + break; + case com_sun_glass_ui_Window_TABBED_BACKDROP: + pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Tabbed); + break; + case com_sun_glass_ui_Window_TRANSIENT_BACKDROP: + pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Transient); + break; + } } return (jlong)hWnd; diff --git a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.h b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.h index 8716aa820f4..6bc0f876615 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.h +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.h @@ -103,6 +103,14 @@ class GlassWindow : public BaseWnd, public ViewContainer { void SetIcon(HICON hIcon); void SetDarkFrame(bool); + + enum BackdropStyle { + Window, + Tabbed, + Transient + }; + void EnableBackdrop(BackdropStyle style); + void HandleWindowPosChangedEvent(); void ShowSystemMenu(int x, int y); @@ -150,6 +158,7 @@ class GlassWindow : public BaseWnd, public ViewContainer { const bool m_isExtended; bool m_isResizable; + bool m_hasBackdrop; BYTE m_alpha; diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/D3DContextInit.cc b/modules/javafx.graphics/src/main/native-prism-d3d/D3DContextInit.cc index 4bb1de681f0..1685972c7c3 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/D3DContextInit.cc +++ b/modules/javafx.graphics/src/main/native-prism-d3d/D3DContextInit.cc @@ -39,6 +39,7 @@ HRESULT D3DContext::InitContext(bool isVsyncEnabled) { params.Windowed = TRUE; params.SwapEffect = D3DSWAPEFFECT_DISCARD; params.hDeviceWindow = GetDesktopWindow(); + params.BackBufferFormat = D3DFMT_A8R8G8B8; params.PresentationInterval = isVsyncEnabled ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/D3DResourceManager.cc b/modules/javafx.graphics/src/main/native-prism-d3d/D3DResourceManager.cc index bcd09ccef61..a6d306a6ee0 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/D3DResourceManager.cc +++ b/modules/javafx.graphics/src/main/native-prism-d3d/D3DResourceManager.cc @@ -556,6 +556,7 @@ D3DResourceManager::CreateSwapChain(HWND hWnd, UINT numBuffers, newParams.hDeviceWindow = hWnd; newParams.Windowed = TRUE; newParams.BackBufferCount = numBuffers; + newParams.BackBufferFormat = D3DFMT_A8R8G8B8; newParams.SwapEffect = swapEffect; newParams.PresentationInterval = presentationInterval; diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java index 28fa61c5a16..1ac41ff02e4 100644 --- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java +++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java @@ -58,6 +58,7 @@ import javafx.stage.FileChooser.ExtensionFilter; import javafx.stage.Modality; import javafx.stage.StageStyle; +import javafx.stage.StageBackdrop; import javafx.stage.Window; import javafx.util.Pair; import com.sun.glass.ui.CommonDialogs.FileChooserResult; @@ -146,7 +147,8 @@ public boolean init() { @Override public TKStage createTKStage(Window peerWindow, StageStyle stageStyle, boolean primary, - Modality modality, TKStage owner, boolean rtl, boolean darkFrame) { + Modality modality, TKStage owner, boolean rtl, + boolean darkFrame, StageBackdrop backdrop) { return new StubStage(); } diff --git a/tests/manual/stage/BackdropTest.java b/tests/manual/stage/BackdropTest.java new file mode 100644 index 00000000000..29054eabc0d --- /dev/null +++ b/tests/manual/stage/BackdropTest.java @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.application.ColorScheme; +import javafx.application.Platform.Preferences; +import javafx.geometry.Pos; +import javafx.geometry.Insets; +import javafx.scene.control.Button; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HeaderBar; +import javafx.scene.paint.Color; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.StageBackdrop; +import javafx.stage.Window; +import java.util.List; + +public class BackdropTest extends Application { + public static void main(String[] args) { + launch(BackdropTest.class, args); + } + + private enum StageStyleChoice { + DECORATED("Decorated", StageStyle.DECORATED), + UNDECORATED("Undecorated", StageStyle.UNDECORATED), + EXTENDED("Extended", StageStyle.EXTENDED), + UTILITY("Utility", StageStyle.UTILITY), + TRANSPARENT("Transparent", StageStyle.TRANSPARENT), + UNIFIED("Unified", StageStyle.UNIFIED); + + private String label; + private StageStyle stageStyle; + + StageStyleChoice(String label, StageStyle style) { + this.label = label; + this.stageStyle = style; + } + + public StageStyle getStageStyle() { + return stageStyle; + } + + public String toString() { + return label; + } + } + + private enum StageBackdropChoice { + DEFAULT("Default", StageBackdrop.DEFAULT), + WINDOW("Window", StageBackdrop.WINDOW), + TABBED("Tabbed", StageBackdrop.TABBED), + TRANSIENT("Transient", StageBackdrop.TRANSIENT); + + private String label; + private StageBackdrop backdrop; + + StageBackdropChoice(String label, StageBackdrop backdrop) { + this.label = label; + this.backdrop = backdrop; + } + + public StageBackdrop getBackdrop() { + return backdrop; + } + + public String toString() { + return label; + } + } + + private enum FillChoice { + NONE("None", null), + TRANSPARENT("Transparent", Color.TRANSPARENT), + BLUE("Light blue", Color.LIGHTBLUE), + TRANSLUCENT_RED("Red (50% opaque)", new Color(1.0, 0.0, 0.0, 0.5)), + TRANSLUCENT_GREEN("Green (20% opaque)", new Color(0.0, 1.0, 0.0, 0.2)); + + private String label; + private Color color; + + FillChoice(String label, Color color) { + this.label = label; + this.color = color; + } + + Color getFill() { + return color; + } + + public String toString() { + return label; + } + } + + private enum ColorSchemeChoice { + LIGHT("Light", ColorScheme.LIGHT), + DARK("Dark", ColorScheme.DARK); + + private String label; + private ColorScheme scheme; + + ColorSchemeChoice(String label, ColorScheme scheme) { + this.label = label; + this.scheme = scheme; + } + + ColorScheme getColorScheme() { + return scheme; + } + + public String toString() { + return label; + } + } + + private enum OpacityChoice { + P100("100%", 1.0), + P75("75%", 0.75), + P50("50%", 0.50); + + private String label; + private double opacity; + + OpacityChoice(String label, double opacity) { + this.label = label; + this.opacity = opacity; + } + + double getOpacity() { + return opacity; + } + + public String toString() { + return label; + } + } + + private Label newLabel(String text) { + var label = new Label(text); + label.textFillProperty().bind(Platform.getPreferences().foregroundColorProperty()); + return label; + } + + private Parent labeledSection(String text, Parent section) { + var label = newLabel(text); + VBox box = new VBox(label, section); + box.setSpacing(5); + return box; + } + + private Parent labeledSection(String text) { + var label = newLabel(text); + VBox box = new VBox(label); + box.setSpacing(5); + return box; + } + + private void buildScene(Stage stage, StageStyleChoice stageStyle, StageBackdropChoice backdrop) { + + var iconifyButton = new Button("Iconify"); + iconifyButton.setOnAction(e -> { + stage.setIconified(!stage.isIconified()); + }); + + var maximizeButton = new Button("Maximize"); + maximizeButton.setOnAction(e -> { + stage.setMaximized(!stage.isMaximized()); + }); + + var fullscreenButton = new Button("Fullscreen"); + fullscreenButton.setOnAction(e -> { + stage.setFullScreen(!stage.isFullScreen()); + }); + + var closeButton = new Button("Close"); + closeButton.setOnAction(e -> { + stage.close(); + }); + + var actionButtons = new HBox(fullscreenButton, maximizeButton, iconifyButton, closeButton); + actionButtons.setSpacing(5); + actionButtons.setAlignment(Pos.BASELINE_RIGHT); + + // For creating new stages + var styleLabel = newLabel("Style"); + ChoiceBox stageStyleChoice = new ChoiceBox<>(); + stageStyleChoice.getItems().setAll(StageStyleChoice.values()); + stageStyleChoice.setValue(stageStyle); + + var backdropLabel = newLabel("backdrop"); + ChoiceBox backdropChoice = new ChoiceBox<>(); + backdropChoice.getItems().setAll(StageBackdropChoice.values()); + backdropChoice.setValue(backdrop); + + Button createButton = new Button("Create!"); + createButton.setOnAction(e -> { + createAndShowStage(stageStyleChoice.getValue(), backdropChoice.getValue()); + }); + + HBox stageCreationControls = new HBox(styleLabel, stageStyleChoice, backdropLabel, backdropChoice, createButton); + stageCreationControls.setAlignment(Pos.BASELINE_LEFT); + stageCreationControls.setSpacing(10); + + ChoiceBox fillChoice = new ChoiceBox<>(); + fillChoice.getItems().setAll(FillChoice.values()); + + ChoiceBox schemeChoice = new ChoiceBox<>(); + schemeChoice.getItems().setAll(ColorSchemeChoice.values()); + + ChoiceBox opacityChoice = new ChoiceBox<>(); + opacityChoice.getItems().setAll(OpacityChoice.values()); + + // Pull it together + VBox controls = new VBox( + labeledSection("This stage is " + stageStyle + " and the backdrop is " + backdrop), + labeledSection("New stage", stageCreationControls), + labeledSection("Fill color for this stage", fillChoice), + labeledSection("Color scheme for this stage", schemeChoice), + labeledSection("Opacity of this stage", opacityChoice) + ); + + controls.setAlignment(Pos.BASELINE_LEFT); + controls.setSpacing(10); + controls.setPadding(new Insets(10, 10, 10, 10)); + + var borderPane = new BorderPane(); + borderPane.setTop(actionButtons); + borderPane.setCenter(controls); + borderPane.setBackground(null); + borderPane.setPadding(new Insets(10, 10, 10, 10)); + borderPane.setOnMousePressed(pressEvent -> { + borderPane.setOnMouseDragged(dragEvent -> { + stage.setX(dragEvent.getScreenX() - pressEvent.getSceneX()); + stage.setY(dragEvent.getScreenY() - pressEvent.getSceneY()); + }); + }); + + Parent root = borderPane; + if (stage.getStyle() == StageStyle.EXTENDED) { + var headerBar = new HeaderBar(); + headerBar.setCenter(new Label(stage.getTitle())); + var box = new VBox(headerBar, borderPane); + box.setBackground(null); + root = box; + } + + Scene scene = new Scene(root, 640, 480, Color.TRANSPARENT); + + fillChoice.setOnAction(e -> { + scene.setFill(fillChoice.getValue().getFill()); + }); + schemeChoice.setOnAction(e -> { + scene.getPreferences().setColorScheme(schemeChoice.getValue().getColorScheme()); + }); + opacityChoice.setOnAction(e -> { + stage.setOpacity(opacityChoice.getValue().getOpacity()); + }); + + fillChoice.setValue(FillChoice.TRANSPARENT); + if (Platform.getPreferences().getColorScheme() == ColorScheme.LIGHT) { + schemeChoice.setValue(ColorSchemeChoice.LIGHT); + } else { + schemeChoice.setValue(ColorSchemeChoice.DARK); + } + opacityChoice.setValue(OpacityChoice.P100); + + stage.setScene(scene); + } + + private void showStage(Stage stage, StageStyleChoice style, StageBackdropChoice backdrop) + { + stage.setTitle(style.toString()); + stage.initStyle(style.getStageStyle()); + stage.initBackdrop(backdrop.getBackdrop()); + buildScene(stage, style, backdrop); + stage.show(); + } + + private void createAndShowStage(StageStyleChoice style, StageBackdropChoice backdrop) + { + Stage stage = new Stage(); + showStage(stage, style, backdrop); + } + + @Override + public void start(Stage stage) { + showStage(stage, StageStyleChoice.EXTENDED, StageBackdropChoice.WINDOW); + } +} From 8cb5e84c5c928281dd350f2c97cf56ec72daaf94 Mon Sep 17 00:00:00 2001 From: Martin Fox Date: Wed, 21 Jan 2026 11:05:16 -0800 Subject: [PATCH 2/4] Removed unused import --- tests/manual/stage/BackdropTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/manual/stage/BackdropTest.java b/tests/manual/stage/BackdropTest.java index 29054eabc0d..2dfea1ef5c3 100644 --- a/tests/manual/stage/BackdropTest.java +++ b/tests/manual/stage/BackdropTest.java @@ -44,7 +44,6 @@ import javafx.stage.StageStyle; import javafx.stage.StageBackdrop; import javafx.stage.Window; -import java.util.List; public class BackdropTest extends Application { public static void main(String[] args) { From b8a9d0a8b7e12cbc0b761fe9ab26e9565a4405ba Mon Sep 17 00:00:00 2001 From: Martin Fox Date: Wed, 8 Apr 2026 10:43:17 -0700 Subject: [PATCH 3/4] Backdrops are now objects created by specifying a material. The list of materials is now open-ended. --- .../java/com/sun/glass/ui/Application.java | 27 ++++++- .../main/java/com/sun/glass/ui/Window.java | 28 ++++--- .../com/sun/glass/ui/gtk/GtkApplication.java | 2 +- .../java/com/sun/glass/ui/gtk/GtkWindow.java | 2 +- .../ui/headless/HeadlessApplication.java | 2 +- .../sun/glass/ui/headless/HeadlessWindow.java | 2 +- .../com/sun/glass/ui/ios/IosApplication.java | 2 +- .../java/com/sun/glass/ui/ios/IosWindow.java | 2 +- .../com/sun/glass/ui/mac/MacApplication.java | 22 ++++- .../java/com/sun/glass/ui/mac/MacWindow.java | 60 +++++++++++++- .../glass/ui/monocle/MonocleApplication.java | 2 +- .../sun/glass/ui/monocle/MonocleWindow.java | 2 +- .../com/sun/glass/ui/win/WinApplication.java | 21 ++++- .../java/com/sun/glass/ui/win/WinWindow.java | 47 ++++++++++- .../main/java/com/sun/javafx/tk/Toolkit.java | 4 + .../sun/javafx/tk/quantum/QuantumToolkit.java | 7 +- .../sun/javafx/tk/quantum/WindowStage.java | 19 ++--- .../src/main/java/javafx/stage/Stage.java | 4 +- .../main/java/javafx/stage/StageBackdrop.java | 64 +++++++++------ .../src/main/native-glass/gtk/GlassWindow.cpp | 2 +- .../src/main/native-glass/ios/GlassWindow.m | 4 +- .../src/main/native-glass/mac/GlassHostView.h | 1 + .../src/main/native-glass/mac/GlassHostView.m | 13 +++ .../src/main/native-glass/mac/GlassWindow.m | 32 +++++--- .../src/main/native-glass/win/GlassWindow.cpp | 15 ++-- tests/manual/stage/BackdropTest.java | 81 ++++++++++++------- 26 files changed, 346 insertions(+), 121 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java index 79fbec649bd..1f0fb3b5a4b 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java @@ -562,7 +562,14 @@ public void menuAboutAction() { * allowed to be of exactly one visual kind, and exactly one functional * type. */ - public abstract Window createWindow(Window owner, Screen screen, int styleMask); + public abstract Window createWindow(Window owner, Screen screen, int styleMask, int backdropID); + + /** + * Create a window with no backdrop. + */ + public final Window createWindow(Window owner, Screen screen, int styleMask) { + return createWindow(owner, screen, styleMask, Window.DEFAULT_BACKDROP_ID); + } /** * Create a window. @@ -575,7 +582,7 @@ public void menuAboutAction() { * type. */ public final Window createWindow(Screen screen, int styleMask) { - return createWindow(null, screen, styleMask); + return createWindow(null, screen, styleMask, Window.DEFAULT_BACKDROP_ID); } public abstract View createView(); @@ -842,4 +849,20 @@ public static void overrideNativeWindowHandle(Class lwFrameWrapperClass, Object public void showDocument(String uri) { _showDocument(uri); } + + /** + * Return the list of backdrop materials supported on this platform. + * The default is an empty list. + */ + public List getBackdropMaterials() { + return List.of(); + } + + /** + * Return the platform identifier for the backdrop as + * indicated by the material. + */ + public int getBackdropIdentifier(String material) { + return Window.DEFAULT_BACKDROP_ID; + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java index 9176ce62b9e..6e52b240996 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java @@ -179,12 +179,9 @@ static protected void remove(Window window) { @Native public static final int DARK_FRAME = 1 << 11; /** - * The backdrop styles take up two bits. 0 is no backdrop. + * The backdrop styles */ - @Native public static final int BACKDROP_MASK = 3 << 12; - @Native public static final int WINDOW_BACKDROP = 1 << 12; - @Native public static final int TABBED_BACKDROP = 2 << 12; - @Native public static final int TRANSIENT_BACKDROP = 3 << 12; + public static final int DEFAULT_BACKDROP_ID = -1; final static public class State { @Native public static final int NORMAL = 1; @@ -219,6 +216,7 @@ public static final class Level { private final int styleMask; private final boolean isDecorated; private final boolean isPopup; + private final int backdropID; protected View view = null; protected Screen screen = null; @@ -254,8 +252,8 @@ public static final class Level { private EventHandler eventHandler; - protected abstract long _createWindow(long ownerPtr, long screenPtr, int mask); - protected Window(Window owner, Screen screen, int styleMask) { + protected abstract long _createWindow(long ownerPtr, long screenPtr, int mask, int backdropID); + protected Window(Window owner, Screen screen, int styleMask, int backdropID) { Application.checkEventThread(); switch (styleMask & (TITLED | TRANSPARENT | EXTENDED)) { case UNTITLED: @@ -289,9 +287,8 @@ protected Window(Window owner, Screen screen, int styleMask) { styleMask &= ~TRANSPARENT; } - if (((styleMask & BACKDROP_MASK) != 0) - && !Application.GetApplication().supportsWindowBackdrops()) { - styleMask &= ~BACKDROP_MASK; + if (!Application.GetApplication().supportsWindowBackdrops()) { + backdropID = DEFAULT_BACKDROP_ID; } this.owner = owner; @@ -299,6 +296,7 @@ protected Window(Window owner, Screen screen, int styleMask) { this.isDecorated = (this.styleMask & Window.TITLED) != 0; this.isPopup = (this.styleMask & Window.POPUP) != 0; this.isModal = (this.styleMask & Window.MODAL) != 0; + this.backdropID = backdropID; this.screen = screen != null ? screen : Screen.getMainScreen(); if (PrismSettings.allowHiDPIScaling) { @@ -309,12 +307,16 @@ protected Window(Window owner, Screen screen, int styleMask) { } this.ptr = _createWindow(owner != null ? owner.getNativeHandle() : 0L, - this.screen.getNativeScreen(), this.styleMask); + this.screen.getNativeScreen(), this.styleMask, this.backdropID); if (this.ptr == 0L) { throw new RuntimeException("could not create platform window"); } } + protected Window(Window owner, Screen screen, int styleMask) { + this(owner, screen, styleMask, DEFAULT_BACKDROP_ID); + } + /** * Specifies the preferred header button height. Sub-classes can use this value in their header button * visualization, but they are not required to accommodate the preferred height. @@ -764,8 +766,8 @@ public boolean isFocused() { } public boolean hasBackdrop() { - // The backdrop flags are only set if backdrops are supported. - return (this.styleMask & Window.BACKDROP_MASK) != 0; + // The backdrop is only set if backdrops are supported. + return (this.backdropID >= 0); } protected abstract boolean _requestFocus(long ptr, int event); diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java index 11898d9b9e5..cb8b35b4ace 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java @@ -330,7 +330,7 @@ protected void _leaveNestedEventLoop(Object retValue) { } @Override - public Window createWindow(Window owner, Screen screen, int styleMask) { + public Window createWindow(Window owner, Screen screen, int styleMask, int backdropID) { return new GtkWindow(owner, screen, styleMask); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java index f0cbe530c40..ad41ff9d5fb 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java @@ -45,7 +45,7 @@ public GtkWindow(Window owner, Screen screen, int styleMask) { } @Override - protected native long _createWindow(long ownerPtr, long screenPtr, int mask); + protected native long _createWindow(long ownerPtr, long screenPtr, int mask, int backdropID); @Override protected native boolean _close(long ptr); diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessApplication.java index fa6e2154899..c78544bb416 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessApplication.java @@ -92,7 +92,7 @@ protected int _isKeyLocked(int keyCode) { } @Override - public Window createWindow(Window owner, Screen screen, int styleMask) { + public Window createWindow(Window owner, Screen screen, int styleMask, int backdropID) { HeadlessWindow window = new HeadlessWindow(windowManager, owner, screen, frameBuffer, styleMask); if (this.activeRobot != null) { activeRobot.windowAdded(window); diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessWindow.java index 9aca2e633f3..6ff5b190afc 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/headless/HeadlessWindow.java @@ -62,7 +62,7 @@ public HeadlessWindow(HeadlessWindowManager wm, Window owner, Screen screen, Byt } @Override - protected long _createWindow(long ownerPtr, long screenPtr, int mask) { + protected long _createWindow(long ownerPtr, long screenPtr, int mask, int backdropID) { this.ptr = ptrCount.incrementAndGet(); return ptr; } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosApplication.java index d9a50f5ab38..e4ee15b22aa 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosApplication.java @@ -69,7 +69,7 @@ private void setEventThread() { * @inheritDoc */ @Override - public Window createWindow(Window owner, Screen screen, int styleMask) { + public Window createWindow(Window owner, Screen screen, int styleMask, int backdropID) { return new IosWindow(owner, screen, styleMask); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosWindow.java index 842536ce38c..a8efce98e94 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosWindow.java @@ -40,7 +40,7 @@ protected IosWindow(Window owner, Screen screen, int styleMask) { } // See Window for documentation - @Override native protected long _createWindow(long ownerPtr, long screenPtr, int mask); + @Override native protected long _createWindow(long ownerPtr, long screenPtr, int mask, int backdropID); @Override native protected boolean _close(long ptr); @Override native protected boolean _setView(long ptr, View view); @Override native protected void _setBounds(long ptr, int x, int y, boolean xSet, boolean ySet, int w, int h, int cw, int ch, float xGravity, float yGravity); diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java index fbb80413061..0ceef5f041a 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java @@ -36,6 +36,7 @@ import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -279,8 +280,8 @@ public Menu getAppleMenu() { // FACTORY METHODS - @Override public Window createWindow(Window owner, Screen screen, int styleMask) { - return new MacWindow(owner, screen, styleMask); + @Override public Window createWindow(Window owner, Screen screen, int styleMask, int backdropID) { + return new MacWindow(owner, screen, styleMask, backdropID); } @Override public View createView() { @@ -547,4 +548,21 @@ public void checkPlatformPreferencesSupport() { protected void _showDocument(String uri) { _openURI(uri); } + + /** + * Return the list of backdrop materials supported on this platform. + * The default is an empty list. + */ + @Override + public List getBackdropMaterials() { + return MacWindow.getBackdropMaterials(); + } + + /** + * Return the platform identifier for the StageBackdrop + */ + @Override + public int getBackdropIdentifier(String material) { + return MacWindow.getBackdropIdentifier(material); + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java index fb995d5ad40..eab0e9cecde 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java @@ -34,6 +34,11 @@ import javafx.geometry.Dimension2D; import javafx.scene.layout.HeaderBar; import java.nio.ByteBuffer; +import java.lang.annotation.Native; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; /** * MacOSX platform implementation class for Window. @@ -45,15 +50,15 @@ final class MacWindow extends Window { _initIDs(); } - protected MacWindow(Window owner, Screen screen, int styleMask) { - super(owner, screen, styleMask); + protected MacWindow(Window owner, Screen screen, int styleMask, int backdropID) { + super(owner, screen, styleMask, backdropID); if (isExtendedWindow()) { prefHeaderButtonHeightProperty().subscribe(this::onPrefHeaderButtonHeightChanged); } } - @Override native protected long _createWindow(long ownerPtr, long screenPtr, int mask); + @Override native protected long _createWindow(long ownerPtr, long screenPtr, int mask, int backdropID); @Override native protected boolean _close(long ptr); @Override native protected boolean _setView(long ptr, View view); @Override native protected boolean _setMenubar(long ptr, long menubarPtr); @@ -220,5 +225,54 @@ static NSWindowToolbarStyle ofHeight(double height) { return SMALL; } } + + final static public class BackdropID { + @Native public static final int WINDOW = 42; + @Native public static final int SIDEBAR = 43; + @Native public static final int MENU = 44; + @Native public static final int GLASS = 100; + @Native public static final int CLEARGLASS = 101; + } + + private static Map backdropMaterials = null; + + private static void initMaterials() { + if (backdropMaterials == null) { + backdropMaterials = new HashMap<>(); + + backdropMaterials.put("Window", BackdropID.WINDOW); + backdropMaterials.put("Partial", BackdropID.SIDEBAR); + + backdropMaterials.put("macOS.Window", BackdropID.WINDOW); + backdropMaterials.put("macOS.Sidebar", BackdropID.SIDEBAR); + backdropMaterials.put("macOS.Menu", BackdropID.MENU); + + // Support for NSGlassEffectView must wait for the macOS 26 SDK + // try { + // var osVers = System.getProperty("os.version"); + // String major = osVers.replaceFirst("(\\d+)\\.\\d+.*", "$1"); + // int v = Integer.parseInt(major); + // if (v >= 26) { + // backdropMaterials.put("macOS.Glass", BackdropID.GLASS); + // backdropMaterials.put("macOS.ClearGlass", BackdropID.CLEARGLASS); + // } + // } catch (Exception e) { + // } + } + } + + public static List getBackdropMaterials() { + initMaterials(); + return new ArrayList<>(backdropMaterials.keySet()); + } + + public static int getBackdropIdentifier(String material) { + initMaterials(); + var id = backdropMaterials.get(material); + if (id == null) { + return Window.DEFAULT_BACKDROP_ID; + } + return id; + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java index e148d17b263..d679fc7b0e2 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java @@ -146,7 +146,7 @@ protected void _leaveNestedEventLoop(Object retValue) { } @Override - public Window createWindow(Window owner, Screen screen, int styleMask) { + public Window createWindow(Window owner, Screen screen, int styleMask, int backdropID) { return new MonocleWindow(owner, screen, styleMask); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java index 41c4246e77f..284bc05354a 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java @@ -158,7 +158,7 @@ private void notifyResizeAndMove(int x, int y, int width, int height) { //creates the native window @Override protected long _createWindow(long NativeWindow, long NativeScreen, - int mask) { + int mask, int backdropID) { id = MonocleWindowManager.getInstance().addWindow(this); return id; } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java index ca0b075e6e2..a19933253c6 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java @@ -214,8 +214,8 @@ protected void runLoop(final Runnable launchable) { // FACTORY METHODS - @Override public Window createWindow(Window owner, Screen screen, int styleMask) { - return new WinWindow(owner, screen, styleMask); + @Override public Window createWindow(Window owner, Screen screen, int styleMask, int backdropID) { + return new WinWindow(owner, screen, styleMask, backdropID); } @Override public View createView() { @@ -448,4 +448,21 @@ public Map> getPlatformKeys() { Map.entry("Windows.NetworkInformation.InternetCostType", String.class) ); } + + /** + * Return the list of backdrop materials supported on this platform. + * The default is an empty list. + */ + @Override + public List getBackdropMaterials() { + return WinWindow.getBackdropMaterials(); + } + + /** + * Return the platform identifier for the StageBackdrop + */ + @Override + public int getBackdropIdentifier(String material) { + return WinWindow.getBackdropIdentifier(material); + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java index 95488f035ae..dd0316e70e1 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java @@ -32,6 +32,13 @@ import com.sun.glass.ui.Screen; import com.sun.glass.ui.View; import com.sun.glass.ui.Window; +import java.lang.annotation.Native; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import javafx.application.Platform; +import javafx.application.ConditionalFeature; /** * MS Windows platform implementation class for Window. @@ -53,8 +60,8 @@ class WinWindow extends Window { _initIDs(); } - protected WinWindow(Window owner, Screen screen, int styleMask) { - super(owner, screen, styleMask); + protected WinWindow(Window owner, Screen screen, int styleMask, int backdropID) { + super(owner, screen, styleMask, backdropID); if (isExtendedWindow()) { prefHeaderButtonHeightProperty().subscribe(this::onPrefHeaderButtonHeightChanged); @@ -290,7 +297,7 @@ public void setDarkFrame(boolean value) { native private long _getInsets(long ptr); native private long _getAnchor(long ptr); native private void _showSystemMenu(long ptr, int x, int y); - @Override native protected long _createWindow(long ownerPtr, long screenPtr, int mask); + @Override native protected long _createWindow(long ownerPtr, long screenPtr, int mask, int backdropID); @Override native protected boolean _close(long ptr); @Override native protected boolean _setView(long ptr, View view); @Override native protected void _updateViewSize(long ptr); @@ -453,4 +460,38 @@ enum HT { case null -> HT.CLIENT.value; }; } + + final static public class BackdropID { + @Native public static final int WINDOW = 50; + @Native public static final int TABBED = 51; + @Native public static final int TRANSIENT = 52; + } + + private static Map backdropMaterials = null; + + private static void initMaterials() { + if (backdropMaterials == null) { + backdropMaterials = new HashMap<>(); + + if (Platform.isSupported(ConditionalFeature.WINDOW_BACKDROP)) { + backdropMaterials.put("Window", BackdropID.WINDOW); + backdropMaterials.put("Partial", BackdropID.TABBED); + backdropMaterials.put("Windows.Transient", BackdropID.TRANSIENT); + } + } + } + + public static List getBackdropMaterials() { + initMaterials(); + return new ArrayList<>(backdropMaterials.keySet()); + } + + public static int getBackdropIdentifier(String material) { + initMaterials(); + var id = backdropMaterials.get(material); + if (id == null) { + return Window.DEFAULT_BACKDROP_ID; + } + return id; + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java index 6606647c226..b087441340e 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java @@ -910,4 +910,8 @@ public String getThemeName() { } public abstract GlassRobot createRobot(); + + public List getBackdropMaterials() { + return List.of(); + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java index d6760236d59..ca0f5225240 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java @@ -701,7 +701,7 @@ private boolean maxNestedEventLoopsHit() { @Override public TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner) { assertToolkitRunning(); - WindowStage stage = new WindowStage(peerWindow, popupStyle, null, owner, false, StageBackdrop.DEFAULT); + WindowStage stage = new WindowStage(peerWindow, popupStyle, null, owner, false, null); stage.setIsPopup(); stage.init(systemMenu); return stage; @@ -1848,4 +1848,9 @@ public int getMultiClickMaxY() { public GlassRobot createRobot() { return com.sun.glass.ui.Application.GetApplication().createRobot(); } + + @Override + public List getBackdropMaterials() { + return Application.GetApplication().getBackdropMaterials(); + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java index c54810f8f5a..d4459be2812 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java @@ -63,7 +63,7 @@ public class WindowStage extends GlassStage { private StageStyle style; private GlassStage owner = null; private Modality modality = Modality.NONE; - private StageBackdrop backdrop = StageBackdrop.DEFAULT; + private StageBackdrop backdrop = null; private OverlayWarning warning = null; private boolean rtl = false; @@ -186,21 +186,12 @@ private void initPlatformWindow() { windowMask |= Window.DARK_FRAME; } - switch (backdrop) { - case DEFAULT: - break; - case WINDOW: - windowMask |= Window.WINDOW_BACKDROP; - break; - case TABBED: - windowMask |= Window.TABBED_BACKDROP; - break; - case TRANSIENT: - windowMask |= Window.TRANSIENT_BACKDROP; - break; + int backdropID = Window.DEFAULT_BACKDROP_ID; + if (backdrop != null) { + backdropID = app.getBackdropIdentifier(backdrop.getMaterial()); } - platformWindow = app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask); + platformWindow = app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask, backdropID); platformWindow.setResizable(resizable); platformWindow.setFocusable(focusable); diff --git a/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java b/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java index 119dd4fcbfb..61e10abdd92 100644 --- a/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java +++ b/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java @@ -567,7 +567,7 @@ public final Window getOwner() { return owner; } - private StageBackdrop backdrop = StageBackdrop.DEFAULT; + private StageBackdrop backdrop = null; /** * Specifies the backdrop for this stage. This must be done prior to @@ -578,7 +578,7 @@ public final Window getOwner() { * @throws IllegalStateException if this property is set after the stage * has ever been made visible. * - * @defaultValue StageBackdrop.DEFAULT + * @defaultValue null */ @SuppressWarnings("deprecation") public final void initBackdrop(StageBackdrop backdrop) { diff --git a/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java b/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java index 3df27eb542b..6c7ce752b9e 100644 --- a/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java +++ b/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java @@ -28,44 +28,60 @@ import javafx.application.ConditionalFeature; import javafx.application.Platform; +import java.util.List; + +import com.sun.javafx.tk.Toolkit; + /** - * This enum defines the possible backdrops for a {@code Stage}. Backdrops are - * typically drawn by the operating system and appear behind the Scene's fill - * and background. The specific effects vary but in general the backdrop will - * track the window's color scheme. + * The backdrop of a {@code Stage}. Backdrops are visual effects drawn across + * the entire stage behind the Scene's fill and background. The specific + * effects vary but in general the backdrop will track the window's color + * scheme. * - * Backdrops are a conditional feature. Currently they are only supported - * on Windows 11 and macOS. + * Platforms which support backdrops will always provide two default + * materials. The "Window" material is appropriate for stages where the + * backdrop effect will be visible across the window. The "Partial" material + * is appropriate for stages where the backdrop will only be partially + * visible. * * @since 27 */ @Deprecated(since = "27") -public enum StageBackdrop { +public final class StageBackdrop { + private String material; - /** - * The default backdrop consistent with earlier versions of JavaFX. - */ - DEFAULT, + private StageBackdrop(String material) { + this.material = material; + } /** - * A backdrop to be used when the backdrop is visible across most of the - * window. This is opaque enough that you can draw text on it using the - * platform's foreground color. + * Gets the backdrop's material + * @return The material of the backdrop */ - WINDOW, + public String getMaterial() { + return material; + } /** - * A backdrop to be used when the backdrop is visible along one edge of - * the window, like behind a sidebar or tab bar. This is opaque enough - * that you can draw text on it using the platform's foreground color. + * Gets all the backdrop materials supported on this system. + * For systems where backdrops are not supported this will + * be an empty list. + * @return The list of all the supported materials. */ - TABBED, + public static List getMaterials() { + return Toolkit.getToolkit().getBackdropMaterials(); + } /** - * A backdrop useful for transient windows or heads-up displays. On some - * platform this backdrop is so translucent that you cannot reliably draw - * text on it. It's recommended that you set the Scene's fill to a - * non-opaque color to overlay on this backdrop. + * Constructs a backdrop using the specified material. + * @param material The material to use for the backdrop. + * @return The backdrop if supported. Otherwise null. */ - TRANSIENT + public static StageBackdrop backdrop(String material) { + if (getMaterials().contains(material)) { + return new StageBackdrop(material); + } + return null; + } } + diff --git a/modules/javafx.graphics/src/main/native-glass/gtk/GlassWindow.cpp b/modules/javafx.graphics/src/main/native-glass/gtk/GlassWindow.cpp index 5707060bb08..b0e90e4ea19 100644 --- a/modules/javafx.graphics/src/main/native-glass/gtk/GlassWindow.cpp +++ b/modules/javafx.graphics/src/main/native-glass/gtk/GlassWindow.cpp @@ -81,7 +81,7 @@ extern "C" { * Signature: (JJI)J */ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1createWindow - (JNIEnv * env, jobject obj, jlong owner, jlong screen, jint mask) + (JNIEnv * env, jobject obj, jlong owner, jlong screen, jint mask, jint backdropID) { (void)env; diff --git a/modules/javafx.graphics/src/main/native-glass/ios/GlassWindow.m b/modules/javafx.graphics/src/main/native-glass/ios/GlassWindow.m index dec2693ae9e..cada98c04a7 100644 --- a/modules/javafx.graphics/src/main/native-glass/ios/GlassWindow.m +++ b/modules/javafx.graphics/src/main/native-glass/ios/GlassWindow.m @@ -944,10 +944,10 @@ - (void) _createWindow /* * Class: com_sun_glass_ui_ios_IosWindow * Method: _createWindow - * Signature: (JJZI)J + * Signature: (JJZII)J */ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_ios_IosWindow__1createWindow -(JNIEnv *env, jobject jwindow, jlong jownerPtr, jlong jscreenPtr, jint jstyleMask) +(JNIEnv *env, jobject jwindow, jlong jownerPtr, jlong jscreenPtr, jint jstyleMask, jint backdropID) { jlong value; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h index eba0d247958..92744a4e532 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.h @@ -34,4 +34,5 @@ } -(void)setJFXView:(NSView*)view; -(void)setBackdrop:(NSVisualEffectMaterial)material; +-(void)setGlassBackdrop:(BOOL)clear; @end diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m index 4b9f469a3d7..4863325987c 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassHostView.m @@ -45,6 +45,19 @@ -(void)setBackdrop:(NSVisualEffectMaterial)material [self addSubview: effect]; } +// Called when the window is first created. +-(void)setGlassBackdrop:(BOOL)clear +{ +#if 0 + // Support for the NSGlassEffectView must wait for the macOS 26 SDK + if (@available(macOS 26.0, *)) { + NSGlassEffectView* effect = [[NSGlassEffectView alloc] initWithFrame: self.bounds]; + effect.style = (clear ? NSGlassEffectViewStyleClear : NSGlassEffectViewStyleRegular); + [self addSubview: effect]; + } +#endif +} + - (void)resizeSubviewsWithOldSize:(NSSize) oldSize { for (NSView* child in self.subviews) { child.frame = self.bounds; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m index 27d84ceab96..58a6a9b9f57 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m @@ -29,6 +29,7 @@ #import "com_sun_glass_ui_Window.h" #import "com_sun_glass_ui_Window_Level.h" #import "com_sun_glass_ui_mac_MacWindow.h" +#import "com_sun_glass_ui_mac_MacWindow_BackdropID.h" #import "GlassMacros.h" #import "GlassWindow.h" @@ -455,7 +456,7 @@ - (void)setJFXView:(GlassView3D*)newView #pragma mark --- Dispatcher // TODO: re-implement using Obj-C blocks ? -static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask) +static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask, jint backdropID) { GlassWindow *window = nil; @@ -598,15 +599,24 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr window->hostView = [[GlassHostView alloc] initWithFrame: NSMakeRect(0, 0, 0, 0)]; window->nsWindow.contentView = window->hostView; - switch (jStyleMask & com_sun_glass_ui_Window_BACKDROP_MASK) { - case com_sun_glass_ui_Window_WINDOW_BACKDROP: + switch (backdropID) { + case com_sun_glass_ui_mac_MacWindow_BackdropID_WINDOW: [window->hostView setBackdrop: NSVisualEffectMaterialUnderWindowBackground]; break; - case com_sun_glass_ui_Window_TABBED_BACKDROP: + case com_sun_glass_ui_mac_MacWindow_BackdropID_SIDEBAR: [window->hostView setBackdrop: NSVisualEffectMaterialSidebar]; break; - case com_sun_glass_ui_Window_TRANSIENT_BACKDROP: - [window->hostView setBackdrop: NSVisualEffectMaterialHUDWindow]; + case com_sun_glass_ui_mac_MacWindow_BackdropID_MENU: + [window->hostView setBackdrop: NSVisualEffectMaterialMenu]; + break; + case com_sun_glass_ui_mac_MacWindow_BackdropID_GLASS: + case com_sun_glass_ui_mac_MacWindow_BackdropID_CLEARGLASS: + [window->hostView setGlassBackdrop: (backdropID == com_sun_glass_ui_mac_MacWindow_BackdropID_CLEARGLASS)]; + [window->nsWindow setBackgroundColor:[NSColor clearColor]]; + [window->nsWindow setHasShadow:NO]; + [window->nsWindow setOpaque: NO]; + // Prevent changes to the background color. + isTransparent = true; break; } @@ -629,7 +639,7 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr } static jlong _createWindowCommon -(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask) +(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask, jint backdropID) { LOG("_createWindowCommon"); @@ -639,7 +649,7 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr GLASS_POOL_ENTER; { jobject jWindowRef = (*env)->NewGlobalRef(env, jWindow); - value = _createWindowCommonDo(env, jWindowRef, jOwnerPtr, jScreenPtr, jStyleMask); + value = _createWindowCommonDo(env, jWindowRef, jOwnerPtr, jScreenPtr, jStyleMask, backdropID); } GLASS_POOL_EXIT; GLASS_CHECK_EXCEPTION(env); @@ -755,14 +765,14 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr /* * Class: com_sun_glass_ui_mac_MacWindow * Method: _createWindow - * Signature: (JJI)J + * Signature: (JJII)J */ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_mac_MacWindow__1createWindow -(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask) +(JNIEnv *env, jobject jWindow, jlong jOwnerPtr, jlong jScreenPtr, jint jStyleMask, jint backdropID) { LOG("Java_com_sun_glass_ui_mac_MacWindow__1createWindow"); - return _createWindowCommon(env, jWindow, jOwnerPtr, jScreenPtr, jStyleMask); + return _createWindowCommon(env, jWindow, jOwnerPtr, jScreenPtr, jStyleMask, backdropID); } /* diff --git a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp index 40f6e83fceb..df5fd8d9889 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp @@ -40,6 +40,7 @@ #include "com_sun_glass_ui_Window.h" #include "com_sun_glass_ui_Window_Level.h" #include "com_sun_glass_ui_win_WinWindow.h" +#include "com_sun_glass_ui_win_WinWindow_BackdropID.h" #define ABM_GETAUTOHIDEBAREX 0x0000000b // multimon aware autohide bars @@ -1586,10 +1587,10 @@ JNIEXPORT void JNICALL Java_com_sun_glass_ui_win_WinWindow__1initIDs /* * Class: com_sun_glass_ui_win_WinWindow * Method: _createWindow - * Signature: (JJZI)J + * Signature: (JJZII)J */ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_win_WinWindow__1createWindow - (JNIEnv *env, jobject jThis, jlong ownerPtr, jlong screenPtr, jint mask) + (JNIEnv *env, jobject jThis, jlong ownerPtr, jlong screenPtr, jint mask, jint backdrop) { ENTER_MAIN_THREAD_AND_RETURN(jlong) { @@ -1670,14 +1671,14 @@ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_win_WinWindow__1createWindow pWindow->SetDarkFrame(true); } - switch (mask & com_sun_glass_ui_Window_BACKDROP_MASK) { - case com_sun_glass_ui_Window_WINDOW_BACKDROP: + switch (backdrop) { + case com_sun_glass_ui_win_WinWindow_BackdropID_WINDOW: pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Window); break; - case com_sun_glass_ui_Window_TABBED_BACKDROP: + case com_sun_glass_ui_win_WinWindow_BackdropID_TABBED: pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Tabbed); break; - case com_sun_glass_ui_Window_TRANSIENT_BACKDROP: + case com_sun_glass_ui_win_WinWindow_BackdropID_TRANSIENT: pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Transient); break; } @@ -1689,12 +1690,14 @@ JNIEXPORT jlong JNICALL Java_com_sun_glass_ui_win_WinWindow__1createWindow HWND owner; HMONITOR hMonitor; jint mask; + jint backdrop; LEAVE_MAIN_THREAD; ARG(jThis) = jThis; ARG(owner) = (HWND)ownerPtr; ARG(hMonitor) = (HMONITOR)screenPtr; ARG(mask) = mask; + ARG(backdrop) = backdrop; return PERFORM_AND_RETURN(); } diff --git a/tests/manual/stage/BackdropTest.java b/tests/manual/stage/BackdropTest.java index 2dfea1ef5c3..ce8e2fb846e 100644 --- a/tests/manual/stage/BackdropTest.java +++ b/tests/manual/stage/BackdropTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ import javafx.application.Application; import javafx.application.Platform; import javafx.application.ColorScheme; -import javafx.application.Platform.Preferences; import javafx.geometry.Pos; import javafx.geometry.Insets; import javafx.scene.control.Button; @@ -44,6 +43,10 @@ import javafx.stage.StageStyle; import javafx.stage.StageBackdrop; import javafx.stage.Window; +import java.util.List; +import java.util.ArrayList; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; public class BackdropTest extends Application { public static void main(String[] args) { @@ -75,20 +78,15 @@ public String toString() { } } - private enum StageBackdropChoice { - DEFAULT("Default", StageBackdrop.DEFAULT), - WINDOW("Window", StageBackdrop.WINDOW), - TABBED("Tabbed", StageBackdrop.TABBED), - TRANSIENT("Transient", StageBackdrop.TRANSIENT); - - private String label; - private StageBackdrop backdrop; - + private class StageBackdropChoice { StageBackdropChoice(String label, StageBackdrop backdrop) { this.label = label; this.backdrop = backdrop; } + private String label; + private StageBackdrop backdrop; + public StageBackdrop getBackdrop() { return backdrop; } @@ -98,6 +96,17 @@ public String toString() { } } + private List backdrops = new ArrayList<>(); + + void initBackdropList() { + backdrops.add(new StageBackdropChoice("Default", null)); + var materials = StageBackdrop.getMaterials(); + materials.sort(null); + materials.forEach(m -> { + backdrops.add(new StageBackdropChoice(m, StageBackdrop.backdrop(m))); + }); + } + private enum FillChoice { NONE("None", null), TRANSPARENT("Transparent", Color.TRANSPARENT), @@ -165,21 +174,33 @@ public String toString() { } } - private Label newLabel(String text) { + private ObjectProperty textColor = new SimpleObjectProperty<>(Color.BLACK); + + private void updateTextColor(Stage stage, ObjectProperty textColor) { + if (stage.getBackdrop() == null) { + textColor.set(Color.BLACK); + } else if (stage.getScene().getPreferences().getColorScheme() == ColorScheme.DARK) { + textColor.set(Color.WHITE); + } else { + textColor.set(Color.BLACK); + } + } + + private Label newLabel(String text, ObjectProperty textColor) { var label = new Label(text); - label.textFillProperty().bind(Platform.getPreferences().foregroundColorProperty()); + label.textFillProperty().bind(textColor); return label; } - private Parent labeledSection(String text, Parent section) { - var label = newLabel(text); + private Parent labeledSection(String text, ObjectProperty textColor, Parent section) { + var label = newLabel(text, textColor); VBox box = new VBox(label, section); box.setSpacing(5); return box; } - private Parent labeledSection(String text) { - var label = newLabel(text); + private Parent labeledSection(String text, ObjectProperty textColor) { + var label = newLabel(text, textColor); VBox box = new VBox(label); box.setSpacing(5); return box; @@ -211,15 +232,17 @@ private void buildScene(Stage stage, StageStyleChoice stageStyle, StageBackdropC actionButtons.setSpacing(5); actionButtons.setAlignment(Pos.BASELINE_RIGHT); + ObjectProperty textColor = new SimpleObjectProperty<>(Color.BLACK); + // For creating new stages - var styleLabel = newLabel("Style"); + var styleLabel = newLabel("Style", textColor); ChoiceBox stageStyleChoice = new ChoiceBox<>(); stageStyleChoice.getItems().setAll(StageStyleChoice.values()); stageStyleChoice.setValue(stageStyle); - var backdropLabel = newLabel("backdrop"); + var backdropLabel = newLabel("backdrop", textColor); ChoiceBox backdropChoice = new ChoiceBox<>(); - backdropChoice.getItems().setAll(StageBackdropChoice.values()); + backdropChoice.getItems().setAll(backdrops); backdropChoice.setValue(backdrop); Button createButton = new Button("Create!"); @@ -242,11 +265,11 @@ private void buildScene(Stage stage, StageStyleChoice stageStyle, StageBackdropC // Pull it together VBox controls = new VBox( - labeledSection("This stage is " + stageStyle + " and the backdrop is " + backdrop), - labeledSection("New stage", stageCreationControls), - labeledSection("Fill color for this stage", fillChoice), - labeledSection("Color scheme for this stage", schemeChoice), - labeledSection("Opacity of this stage", opacityChoice) + labeledSection("This stage is " + stageStyle + " and the backdrop is " + backdrop, textColor), + labeledSection("New stage", textColor, stageCreationControls), + labeledSection("Fill color for this stage", textColor, fillChoice), + labeledSection("Color scheme for this stage", textColor, schemeChoice), + labeledSection("Opacity of this stage", textColor, opacityChoice) ); controls.setAlignment(Pos.BASELINE_LEFT); @@ -275,12 +298,15 @@ private void buildScene(Stage stage, StageStyleChoice stageStyle, StageBackdropC } Scene scene = new Scene(root, 640, 480, Color.TRANSPARENT); + stage.setScene(scene); fillChoice.setOnAction(e -> { scene.setFill(fillChoice.getValue().getFill()); + updateTextColor(stage, textColor); }); schemeChoice.setOnAction(e -> { scene.getPreferences().setColorScheme(schemeChoice.getValue().getColorScheme()); + updateTextColor(stage, textColor); }); opacityChoice.setOnAction(e -> { stage.setOpacity(opacityChoice.getValue().getOpacity()); @@ -294,7 +320,7 @@ private void buildScene(Stage stage, StageStyleChoice stageStyle, StageBackdropC } opacityChoice.setValue(OpacityChoice.P100); - stage.setScene(scene); + updateTextColor(stage, textColor); } private void showStage(Stage stage, StageStyleChoice style, StageBackdropChoice backdrop) @@ -314,6 +340,7 @@ private void createAndShowStage(StageStyleChoice style, StageBackdropChoice back @Override public void start(Stage stage) { - showStage(stage, StageStyleChoice.EXTENDED, StageBackdropChoice.WINDOW); + initBackdropList(); + showStage(stage, StageStyleChoice.EXTENDED, backdrops.get(0)); } } From c051017aa5172dad2825a561a69921309b3eb9c7 Mon Sep 17 00:00:00 2001 From: Martin Fox Date: Wed, 20 May 2026 07:06:51 -0700 Subject: [PATCH 4/4] New version of StageBackdrop API, separated into standard and platform variants commit dee190390d147e9032a734ab0abaab752e308c76 Author: Martin Fox Date: Wed May 20 06:18:23 2026 -0700 More minor cleanup commit 457c7eb99affd42f67dedb2c760fa639a88943b9 Author: Martin Fox Date: Tue May 19 19:33:15 2026 -0700 Cleaning up imports, comments, etc. commit 947a5e3598fd0e1b8f69747314405519a8a8794d Author: Martin Fox Date: Tue May 19 16:16:23 2026 -0700 Disabling macOS 26 Liquid Glass effect commit 9c3d0389f3687504411fc840ecb878ebe9608e36 Author: Martin Fox Date: Tue May 19 16:14:19 2026 -0700 Backdrop options are now dynamic and set on the stage. commit 7d61c2b9bc15a7bdc7c79b0ddde651678404f065 Author: Martin Fox Date: Tue May 19 11:15:09 2026 -0700 Added options query and simplified Windows implementation commit c1514f348f9e1d9d3761aec9786dda25e12fa588 Author: Martin Fox Date: Mon May 18 12:07:40 2026 -0700 Revamped StageBackdrop API, splitting standard and platform backdrops and adding options commit 608cfa631967470bdbfc02a5b30e55ab3c932d32 Merge: 551c9ec336 7110a0940d Author: Martin Fox Date: Thu May 14 07:08:44 2026 -0700 Merge branch 'osbackdrop' into osbackdrop_dev commit 551c9ec336d9e6d3f90e37b278dc17df9a9bca03 Author: Martin Fox Date: Wed Apr 8 08:51:49 2026 -0700 Cleanup. commit c2e8ec8afc4df2dbd529470761b500e4d670bbeb Author: Martin Fox Date: Sat Apr 4 20:18:07 2026 -0700 Factory for construction backdrops commit 4654e4071e19b0b45e44161925b38904d100dda5 Author: Martin Fox Date: Wed Apr 1 16:20:29 2026 -0700 Some tests call Application.createWindow. Added compatibility version. commit cb417164e378a5e15c9d9b62b80bd010495b1f28 Author: Martin Fox Date: Wed Apr 1 12:34:00 2026 -0700 Removing macOS 26 glass backdrops since they require an updated SDK commit 96f5ab2250b911d5fd5436e865850a4b17e07e62 Author: Martin Fox Date: Wed Apr 1 12:16:39 2026 -0700 JavaDoc fixes, Ios fixes commit 547027b89fa6f22c8801fda9b878f9a9ac91fbc3 Author: Martin Fox Date: Wed Apr 1 11:43:47 2026 -0700 Windows implementation of material-based backdrops commit 3b1b47917baf6a36a01199dc9fa18fdc076bab52 Author: Martin Fox Date: Wed Apr 1 09:44:31 2026 -0700 Backdrop materials are now strings and can be platform-specific. --- .../java/com/sun/glass/ui/Application.java | 26 ++++-- .../main/java/com/sun/glass/ui/Window.java | 8 +- .../com/sun/glass/ui/mac/MacApplication.java | 22 ++--- .../java/com/sun/glass/ui/mac/MacWindow.java | 63 ++++++++----- .../com/sun/glass/ui/win/WinApplication.java | 25 ++--- .../java/com/sun/glass/ui/win/WinWindow.java | 39 ++++---- .../javafx/stage/PlatformStageBackdrop.java | 68 ++++++++++++++ .../javafx/stage/StandardStageBackdrop.java | 46 +++++++++ .../main/java/com/sun/javafx/tk/Toolkit.java | 6 +- .../sun/javafx/tk/quantum/QuantumToolkit.java | 9 +- .../sun/javafx/tk/quantum/WindowStage.java | 4 +- .../src/main/java/javafx/stage/Stage.java | 16 ++++ .../main/java/javafx/stage/StageBackdrop.java | 93 +++++++++++++------ .../src/main/native-glass/mac/GlassWindow.m | 3 +- tests/manual/stage/BackdropTest.java | 30 ++++-- 15 files changed, 339 insertions(+), 119 deletions(-) create mode 100644 modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PlatformStageBackdrop.java create mode 100644 modules/javafx.graphics/src/main/java/com/sun/javafx/stage/StandardStageBackdrop.java diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java index 1f0fb3b5a4b..7f4f74ff8e8 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java @@ -28,10 +28,14 @@ import com.sun.glass.ui.CommonDialogs.ExtensionFilter; import com.sun.glass.ui.CommonDialogs.FileChooserResult; import com.sun.javafx.application.preferences.PreferenceMapping; +import com.sun.javafx.stage.PlatformStageBackdrop; + +import javafx.stage.StageBackdrop; import java.io.File; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.LinkedList; @@ -568,7 +572,7 @@ public void menuAboutAction() { * Create a window with no backdrop. */ public final Window createWindow(Window owner, Screen screen, int styleMask) { - return createWindow(owner, screen, styleMask, Window.DEFAULT_BACKDROP_ID); + return createWindow(owner, screen, styleMask, Window.NO_BACKDROP_ID); } /** @@ -582,7 +586,7 @@ public final Window createWindow(Window owner, Screen screen, int styleMask) { * type. */ public final Window createWindow(Screen screen, int styleMask) { - return createWindow(null, screen, styleMask, Window.DEFAULT_BACKDROP_ID); + return createWindow(null, screen, styleMask, Window.NO_BACKDROP_ID); } public abstract View createView(); @@ -851,18 +855,24 @@ public void showDocument(String uri) { } /** - * Return the list of backdrop materials supported on this platform. + * Return the list of backdrops supported on this platform. * The default is an empty list. */ - public List getBackdropMaterials() { + public List getPlatformBackdropNames() { return List.of(); } /** - * Return the platform identifier for the backdrop as - * indicated by the material. + * Create a Platform backdrop for the specified name + */ + public PlatformStageBackdrop createPlatformBackdrop(String name) { + return null; + } + + /** + * Return the platform identifier for the backdrop */ - public int getBackdropIdentifier(String material) { - return Window.DEFAULT_BACKDROP_ID; + public int getBackdropIdentifier(StageBackdrop backdrop) { + return Window.NO_BACKDROP_ID; } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java index 6e52b240996..9f3a6b16c35 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java @@ -179,9 +179,9 @@ static protected void remove(Window window) { @Native public static final int DARK_FRAME = 1 << 11; /** - * The backdrop styles + * Indicates the window has no backdrop */ - public static final int DEFAULT_BACKDROP_ID = -1; + public static final int NO_BACKDROP_ID = -1; final static public class State { @Native public static final int NORMAL = 1; @@ -288,7 +288,7 @@ protected Window(Window owner, Screen screen, int styleMask, int backdropID) { } if (!Application.GetApplication().supportsWindowBackdrops()) { - backdropID = DEFAULT_BACKDROP_ID; + backdropID = NO_BACKDROP_ID; } this.owner = owner; @@ -314,7 +314,7 @@ protected Window(Window owner, Screen screen, int styleMask, int backdropID) { } protected Window(Window owner, Screen screen, int styleMask) { - this(owner, screen, styleMask, DEFAULT_BACKDROP_ID); + this(owner, screen, styleMask, NO_BACKDROP_ID); } /** diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java index 0ceef5f041a..d6515173cc3 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java @@ -29,8 +29,10 @@ import com.sun.glass.ui.CommonDialogs.ExtensionFilter; import com.sun.glass.ui.CommonDialogs.FileChooserResult; import com.sun.javafx.application.preferences.PreferenceMapping; +import com.sun.javafx.stage.PlatformStageBackdrop; import com.sun.javafx.util.Logging; import javafx.scene.paint.Color; +import javafx.stage.StageBackdrop; import java.io.File; import java.nio.ByteBuffer; @@ -549,20 +551,18 @@ protected void _showDocument(String uri) { _openURI(uri); } - /** - * Return the list of backdrop materials supported on this platform. - * The default is an empty list. - */ @Override - public List getBackdropMaterials() { - return MacWindow.getBackdropMaterials(); + public List getPlatformBackdropNames() { + return MacWindow.getPlatformBackdropNames(); + } + + @Override + public PlatformStageBackdrop createPlatformBackdrop(String name) { + return MacWindow.createPlatformBackdrop(name); } - /** - * Return the platform identifier for the StageBackdrop - */ @Override - public int getBackdropIdentifier(String material) { - return MacWindow.getBackdropIdentifier(material); + public int getBackdropIdentifier(StageBackdrop backdrop) { + return MacWindow.getBackdropIdentifier(backdrop); } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java index eab0e9cecde..f0f045e0eff 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java @@ -31,12 +31,18 @@ import com.sun.glass.ui.Screen; import com.sun.glass.ui.View; import com.sun.glass.ui.Window; +import com.sun.javafx.stage.PlatformStageBackdrop; + import javafx.geometry.Dimension2D; import javafx.scene.layout.HeaderBar; +import javafx.scene.paint.Color; +import javafx.stage.StageBackdrop; import java.nio.ByteBuffer; import java.lang.annotation.Native; import java.util.List; +import java.util.Collections; import java.util.ArrayList; +import java.util.Arrays; import java.util.Map; import java.util.HashMap; @@ -230,22 +236,18 @@ final static public class BackdropID { @Native public static final int WINDOW = 42; @Native public static final int SIDEBAR = 43; @Native public static final int MENU = 44; - @Native public static final int GLASS = 100; - @Native public static final int CLEARGLASS = 101; + @Native public static final int CLEARGLASS = 100; } - private static Map backdropMaterials = null; - - private static void initMaterials() { - if (backdropMaterials == null) { - backdropMaterials = new HashMap<>(); + private static Map backdrops = null; - backdropMaterials.put("Window", BackdropID.WINDOW); - backdropMaterials.put("Partial", BackdropID.SIDEBAR); + private static void initBackdrops() { + if (backdrops == null) { + backdrops = new HashMap<>(); - backdropMaterials.put("macOS.Window", BackdropID.WINDOW); - backdropMaterials.put("macOS.Sidebar", BackdropID.SIDEBAR); - backdropMaterials.put("macOS.Menu", BackdropID.MENU); + backdrops.put("macOS.Window", BackdropID.WINDOW); + backdrops.put("macOS.Sidebar", BackdropID.SIDEBAR); + backdrops.put("macOS.Menu", BackdropID.MENU); // Support for NSGlassEffectView must wait for the macOS 26 SDK // try { @@ -253,24 +255,43 @@ private static void initMaterials() { // String major = osVers.replaceFirst("(\\d+)\\.\\d+.*", "$1"); // int v = Integer.parseInt(major); // if (v >= 26) { - // backdropMaterials.put("macOS.Glass", BackdropID.GLASS); - // backdropMaterials.put("macOS.ClearGlass", BackdropID.CLEARGLASS); + // backdrops.put("macOS.ClearGlass", BackdropID.CLEARGLASS); // } // } catch (Exception e) { // } } } - public static List getBackdropMaterials() { - initMaterials(); - return new ArrayList<>(backdropMaterials.keySet()); + public static List getPlatformBackdropNames() { + initBackdrops(); + return Collections.unmodifiableList(new ArrayList<>(backdrops.keySet())); + } + + public static PlatformStageBackdrop createPlatformBackdrop(String name) { + initBackdrops(); + var id = backdrops.get(name); + if (id == null) { + return null; + } + var backdrop = new PlatformStageBackdrop(name); + if (name == "macOS.ClearGlass") { + backdrop.setAvailableOptions(Map.of("TintColor", Color.class, "CornerRadius", Number.class)); + } + + return backdrop; } - public static int getBackdropIdentifier(String material) { - initMaterials(); - var id = backdropMaterials.get(material); + public static int getBackdropIdentifier(StageBackdrop backdrop) { + if (backdrop == StageBackdrop.WINDOW) { + return BackdropID.WINDOW; + } else if (backdrop == StageBackdrop.PARTIAL) { + return BackdropID.SIDEBAR; + } + + initBackdrops(); + var id = backdrops.get(backdrop.getName()); if (id == null) { - return Window.DEFAULT_BACKDROP_ID; + return Window.NO_BACKDROP_ID; } return id; } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java index a19933253c6..6b6f0ae06a7 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java @@ -28,9 +28,11 @@ import com.sun.glass.ui.CommonDialogs.ExtensionFilter; import com.sun.glass.ui.CommonDialogs.FileChooserResult; import com.sun.javafx.application.preferences.PreferenceMapping; +import com.sun.javafx.stage.PlatformStageBackdrop; import com.sun.prism.impl.PrismSettings; import com.sun.javafx.tk.Toolkit; import javafx.scene.paint.Color; +import javafx.stage.StageBackdrop; import java.io.File; import java.nio.ByteBuffer; @@ -449,20 +451,21 @@ public Map> getPlatformKeys() { ); } - /** - * Return the list of backdrop materials supported on this platform. - * The default is an empty list. - */ @Override - public List getBackdropMaterials() { - return WinWindow.getBackdropMaterials(); + public List getPlatformBackdropNames() { + return WinWindow.getPlatformBackdropNames(); } - /** - * Return the platform identifier for the StageBackdrop - */ @Override - public int getBackdropIdentifier(String material) { - return WinWindow.getBackdropIdentifier(material); + public PlatformStageBackdrop createPlatformBackdrop(String name) { + if (getPlatformBackdropNames().contains(name)) { + return new PlatformStageBackdrop(name); + } + return null; + } + + @Override + public int getBackdropIdentifier(StageBackdrop backdrop) { + return WinWindow.getBackdropIdentifier(backdrop); } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java index dd0316e70e1..b7343f906e8 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java @@ -33,12 +33,14 @@ import com.sun.glass.ui.View; import com.sun.glass.ui.Window; import java.lang.annotation.Native; +import java.util.Collections; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import javafx.application.Platform; import javafx.application.ConditionalFeature; +import javafx.stage.StageBackdrop; /** * MS Windows platform implementation class for Window. @@ -467,31 +469,28 @@ final static public class BackdropID { @Native public static final int TRANSIENT = 52; } - private static Map backdropMaterials = null; + private static final String TRANSIENT_NAME = "Windows.Transient"; - private static void initMaterials() { - if (backdropMaterials == null) { - backdropMaterials = new HashMap<>(); - - if (Platform.isSupported(ConditionalFeature.WINDOW_BACKDROP)) { - backdropMaterials.put("Window", BackdropID.WINDOW); - backdropMaterials.put("Partial", BackdropID.TABBED); - backdropMaterials.put("Windows.Transient", BackdropID.TRANSIENT); - } + public static List getPlatformBackdropNames() { + if (Platform.isSupported(ConditionalFeature.WINDOW_BACKDROP)) { + return List.of(TRANSIENT_NAME); } + return List.of(); } - public static List getBackdropMaterials() { - initMaterials(); - return new ArrayList<>(backdropMaterials.keySet()); - } + public static int getBackdropIdentifier(StageBackdrop backdrop) { + if (!Platform.isSupported(ConditionalFeature.WINDOW_BACKDROP)) { + return Window.NO_BACKDROP_ID; + } - public static int getBackdropIdentifier(String material) { - initMaterials(); - var id = backdropMaterials.get(material); - if (id == null) { - return Window.DEFAULT_BACKDROP_ID; + if (backdrop == StageBackdrop.WINDOW) { + return BackdropID.WINDOW; + } else if (backdrop == StageBackdrop.PARTIAL) { + return BackdropID.TABBED; + } else if (backdrop.getName() == TRANSIENT_NAME) { + return BackdropID.TRANSIENT; } - return id; + + return Window.NO_BACKDROP_ID; } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PlatformStageBackdrop.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PlatformStageBackdrop.java new file mode 100644 index 00000000000..71547ce6b51 --- /dev/null +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PlatformStageBackdrop.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.stage; + +import javafx.stage.StageBackdrop; +import java.util.Map; + +/** + * A PlatformStageBackdrop tracks the name and an optional map detailing + * the options available for this backdrop. + */ +public final class PlatformStageBackdrop implements StageBackdrop { + public String name; + public Map> options; + + public PlatformStageBackdrop(String name) { + this.name = name; + this.options = Map.of(); + } + + @Override + public String getName() { + return name; + } + + @Override + public Map> getAvailableOptions() { + return options; + } + + public void setAvailableOptions(Map> o) { + options = o; + } + + @Override + public String toString() { + String klassName = getClass().getName(); + String simpleName = klassName.substring(klassName.lastIndexOf('.')+1); + StringBuilder sbuf = new StringBuilder(simpleName); + sbuf.append("[name="); + sbuf.append(getName()); + sbuf.append("]"); + return sbuf.toString(); + } +} diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/StandardStageBackdrop.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/StandardStageBackdrop.java new file mode 100644 index 00000000000..f39698a3c2d --- /dev/null +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/StandardStageBackdrop.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.javafx.stage; + +import javafx.stage.StageBackdrop; + +/** + * + */ +public enum StandardStageBackdrop implements StageBackdrop { + WINDOW("Window"), + PARTIAL("Partial"); + + private String name; + + private StandardStageBackdrop(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java index b087441340e..cae0f33c29d 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java @@ -911,7 +911,11 @@ public String getThemeName() { public abstract GlassRobot createRobot(); - public List getBackdropMaterials() { + public List getPlatformBackdropNames() { return List.of(); } + + public StageBackdrop createPlatformBackdrop(String name) { + return null; + } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java index ca0f5225240..b784572237a 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java @@ -1850,7 +1850,12 @@ public GlassRobot createRobot() { } @Override - public List getBackdropMaterials() { - return Application.GetApplication().getBackdropMaterials(); + public List getPlatformBackdropNames() { + return Application.GetApplication().getPlatformBackdropNames(); + } + + @Override + public StageBackdrop createPlatformBackdrop(String name) { + return Application.GetApplication().createPlatformBackdrop(name); } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java index d4459be2812..0417835ef68 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/WindowStage.java @@ -186,9 +186,9 @@ private void initPlatformWindow() { windowMask |= Window.DARK_FRAME; } - int backdropID = Window.DEFAULT_BACKDROP_ID; + int backdropID = Window.NO_BACKDROP_ID; if (backdrop != null) { - backdropID = app.getBackdropIdentifier(backdrop.getMaterial()); + backdropID = app.getBackdropIdentifier(backdrop); } platformWindow = app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask, backdropID); diff --git a/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java b/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java index 61e10abdd92..9f3abc08d09 100644 --- a/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java +++ b/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java @@ -600,6 +600,22 @@ public final StageBackdrop getBackdrop() { return backdrop; } + /** + * Set an option for a backdrop. + * + * @param name The name of the option + * @param option The new value of the option + */ + public final void setBackdropOption(String name, Object option) { + if (backdrop != null) { + var avail = backdrop.getAvailableOptions(); + var optionClass = avail.get(name); + if (optionClass != null && + optionClass.isInstance(option)) { + } + } + } + /** * Specifies whether this {@code Stage} should be a full-screen, * undecorated window. diff --git a/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java b/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java index 6c7ce752b9e..589020c3fb5 100644 --- a/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java +++ b/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,59 +29,96 @@ import javafx.application.Platform; import java.util.List; +import java.util.Map; import com.sun.javafx.tk.Toolkit; +import com.sun.javafx.stage.StandardStageBackdrop; +import com.sun.javafx.stage.PlatformStageBackdrop; /** * The backdrop of a {@code Stage}. Backdrops are visual effects drawn across * the entire stage behind the Scene's fill and background. The specific * effects vary but in general the backdrop will track the window's color - * scheme. + * scheme. The effect is drawn behind the Scene's fill and background. * - * Platforms which support backdrops will always provide two default - * materials. The "Window" material is appropriate for stages where the - * backdrop effect will be visible across the window. The "Partial" material - * is appropriate for stages where the backdrop will only be partially - * visible. + *

+ * + * Backdrops are not supported on all platforms. To check if it is supported + * see {@link javafx.application.ConditionalFeature#WINDOW_BACKDROP + * ConditionalFeature.WINDOW_BACKDROP}. + * + *

+ * + * Platforms which support backdrops will always provide two standard + * backdrops, WINDOW and PARTIAL. A platform may also provide a set of + * backdrops specific to that platform. + * + *

+ * + * Backdrop objects are immutable and can be shared across stages. * * @since 27 */ @Deprecated(since = "27") -public final class StageBackdrop { - private String material; +public sealed interface StageBackdrop permits StandardStageBackdrop, PlatformStageBackdrop { + + /** + * Gets the backdrop's name + * + * @return The name of the backdrop. + */ + public String getName(); - private StageBackdrop(String material) { - this.material = material; + /** + * Returns a map where each key is the name of a supported backdrop option + * and each value is the class of object allowed when setting the option. + * + * @return A map of the available option names and classes. + */ + default public Map> getAvailableOptions() { + return Map.of(); } /** - * Gets the backdrop's material - * @return The material of the backdrop + * Defines a {@code StageBackdrop} appropriate when the backdrop will be + * visible across the entire stage. + */ + StageBackdrop WINDOW = StandardStageBackdrop.WINDOW; + + /** + * Defines a {@code StageBackdrop} appropriate when the backdrop will be + * visible only along one edge of the stage, perhaps in a tab bar or side + * panel. + */ + StageBackdrop PARTIAL = StandardStageBackdrop.PARTIAL; + + /** + * Gets a list of the standard backdrops. + * + * @return A list containing the standard backdrops. */ - public String getMaterial() { - return material; + public static List getStandardBackdrops() { + return List.of(WINDOW, PARTIAL); } /** - * Gets all the backdrop materials supported on this system. - * For systems where backdrops are not supported this will - * be an empty list. - * @return The list of all the supported materials. + * Gets the names of platform backdrops supported on this system. The list + * may be empty. + * + * @return An unmodifiable list of names of the supported platform backdrops. */ - public static List getMaterials() { - return Toolkit.getToolkit().getBackdropMaterials(); + public static List getPlatformBackdropNames() { + return Toolkit.getToolkit().getPlatformBackdropNames(); } /** - * Constructs a backdrop using the specified material. - * @param material The material to use for the backdrop. + * Creates a platform backdrop for the specified name. + * + * @param name The name of the backdrop. * @return The backdrop if supported. Otherwise null. */ - public static StageBackdrop backdrop(String material) { - if (getMaterials().contains(material)) { - return new StageBackdrop(material); - } - return null; + public static StageBackdrop backdrop(String name) { + return Toolkit.getToolkit().createPlatformBackdrop(name); } } diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m index 58a6a9b9f57..7252a0ae7da 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m @@ -609,9 +609,8 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr case com_sun_glass_ui_mac_MacWindow_BackdropID_MENU: [window->hostView setBackdrop: NSVisualEffectMaterialMenu]; break; - case com_sun_glass_ui_mac_MacWindow_BackdropID_GLASS: case com_sun_glass_ui_mac_MacWindow_BackdropID_CLEARGLASS: - [window->hostView setGlassBackdrop: (backdropID == com_sun_glass_ui_mac_MacWindow_BackdropID_CLEARGLASS)]; + [window->hostView setGlassBackdrop: YES]; [window->nsWindow setBackgroundColor:[NSColor clearColor]]; [window->nsWindow setHasShadow:NO]; [window->nsWindow setOpaque: NO]; diff --git a/tests/manual/stage/BackdropTest.java b/tests/manual/stage/BackdropTest.java index ce8e2fb846e..7164107ffe8 100644 --- a/tests/manual/stage/BackdropTest.java +++ b/tests/manual/stage/BackdropTest.java @@ -44,6 +44,8 @@ import javafx.stage.StageBackdrop; import javafx.stage.Window; import java.util.List; +import java.util.Map; +import java.util.HashMap; import java.util.ArrayList; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -79,9 +81,9 @@ public String toString() { } private class StageBackdropChoice { - StageBackdropChoice(String label, StageBackdrop backdrop) { - this.label = label; + StageBackdropChoice(StageBackdrop backdrop) { this.backdrop = backdrop; + this.label = (backdrop == null ? "None" : backdrop.getName()); } private String label; @@ -99,11 +101,14 @@ public String toString() { private List backdrops = new ArrayList<>(); void initBackdropList() { - backdrops.add(new StageBackdropChoice("Default", null)); - var materials = StageBackdrop.getMaterials(); - materials.sort(null); - materials.forEach(m -> { - backdrops.add(new StageBackdropChoice(m, StageBackdrop.backdrop(m))); + backdrops.add(new StageBackdropChoice(null)); + backdrops.add(new StageBackdropChoice(StageBackdrop.WINDOW)); + backdrops.add(new StageBackdropChoice(StageBackdrop.PARTIAL)); + var names = new ArrayList<>(StageBackdrop.getPlatformBackdropNames()); + names.sort(null); + names.forEach(m -> { + var backdrop = StageBackdrop.backdrop(m); + backdrops.add(new StageBackdropChoice(backdrop)); }); } @@ -319,6 +324,10 @@ private void buildScene(Stage stage, StageStyleChoice stageStyle, StageBackdropC schemeChoice.setValue(ColorSchemeChoice.DARK); } opacityChoice.setValue(OpacityChoice.P100); + var ob = backdrops.stream() + .filter(obj -> obj.getBackdrop() == StageBackdrop.WINDOW) + .findFirst(); + ob.ifPresent(pb -> backdropChoice.setValue(pb)); updateTextColor(stage, textColor); } @@ -327,7 +336,7 @@ private void showStage(Stage stage, StageStyleChoice style, StageBackdropChoice { stage.setTitle(style.toString()); stage.initStyle(style.getStageStyle()); - stage.initBackdrop(backdrop.getBackdrop()); + stage.initBackdrop(backdrop == null ? null : backdrop.getBackdrop()); buildScene(stage, style, backdrop); stage.show(); } @@ -341,6 +350,9 @@ private void createAndShowStage(StageStyleChoice style, StageBackdropChoice back @Override public void start(Stage stage) { initBackdropList(); - showStage(stage, StageStyleChoice.EXTENDED, backdrops.get(0)); + var ob = backdrops.stream() + .filter(obj -> obj.getBackdrop() == StageBackdrop.WINDOW) + .findFirst(); + showStage(stage, StageStyleChoice.EXTENDED, ob.orElse(null)); } }