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 efe03d9fd8d..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; @@ -562,7 +566,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.NO_BACKDROP_ID); + } /** * Create a window. @@ -575,7 +586,7 @@ public void menuAboutAction() { * type. */ public final Window createWindow(Screen screen, int styleMask) { - return createWindow(null, screen, styleMask); + return createWindow(null, screen, styleMask, Window.NO_BACKDROP_ID); } public abstract View createView(); @@ -719,6 +730,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; @@ -833,4 +853,26 @@ public static void overrideNativeWindowHandle(Class lwFrameWrapperClass, Object public void showDocument(String uri) { _showDocument(uri); } + + /** + * Return the list of backdrops supported on this platform. + * The default is an empty list. + */ + public List getPlatformBackdropNames() { + return List.of(); + } + + /** + * 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(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 e11cee2d21b..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 @@ -178,6 +178,11 @@ static protected void remove(Window window) { */ @Native public static final int DARK_FRAME = 1 << 11; + /** + * Indicates the window has no backdrop + */ + public static final int NO_BACKDROP_ID = -1; + final static public class State { @Native public static final int NORMAL = 1; @Native public static final int MINIMIZED = 2; @@ -211,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; @@ -246,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: @@ -281,11 +287,16 @@ protected Window(Window owner, Screen screen, int styleMask) { styleMask &= ~TRANSPARENT; } + if (!Application.GetApplication().supportsWindowBackdrops()) { + backdropID = NO_BACKDROP_ID; + } + this.owner = owner; this.styleMask = 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) { @@ -296,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, NO_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. @@ -750,6 +765,11 @@ public boolean isFocused() { return this.isFocused; } + public boolean hasBackdrop() { + // The backdrop is only set if backdrops are supported. + return (this.backdropID >= 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/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 82b6c8fd506..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,13 +29,16 @@ 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; import java.nio.IntBuffer; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -279,8 +282,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() { @@ -404,6 +407,11 @@ protected boolean _supportsExtendedWindows() { return true; } + @Override + protected boolean _supportsWindowBackdrops() { + return true; + } + @Override protected boolean _supportsSystemMenu() { return true; } @@ -542,4 +550,19 @@ public void checkPlatformPreferencesSupport() { protected void _showDocument(String uri) { _openURI(uri); } + + @Override + public List getPlatformBackdropNames() { + return MacWindow.getPlatformBackdropNames(); + } + + @Override + public PlatformStageBackdrop createPlatformBackdrop(String name) { + return MacWindow.createPlatformBackdrop(name); + } + + @Override + 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 fb995d5ad40..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,9 +31,20 @@ 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; /** * MacOSX platform implementation class for Window. @@ -45,15 +56,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 +231,69 @@ 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 CLEARGLASS = 100; + } + + private static Map backdrops = null; + + private static void initBackdrops() { + if (backdrops == null) { + backdrops = new HashMap<>(); + + 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 { + // var osVers = System.getProperty("os.version"); + // String major = osVers.replaceFirst("(\\d+)\\.\\d+.*", "$1"); + // int v = Integer.parseInt(major); + // if (v >= 26) { + // backdrops.put("macOS.ClearGlass", BackdropID.CLEARGLASS); + // } + // } catch (Exception e) { + // } + } + } + + 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(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.NO_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 eb9586fa54a..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; @@ -214,8 +216,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() { @@ -380,6 +382,8 @@ protected boolean _supportsExtendedWindows() { return true; } + @Override native protected boolean _supportsWindowBackdrops(); + @Override public String getDataDirectory() { checkEventThread(); @@ -446,4 +450,22 @@ public Map> getPlatformKeys() { Map.entry("Windows.NetworkInformation.InternetCostType", String.class) ); } + + @Override + public List getPlatformBackdropNames() { + return WinWindow.getPlatformBackdropNames(); + } + + @Override + 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 95488f035ae..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 @@ -32,6 +32,15 @@ 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.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. @@ -53,8 +62,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 +299,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 +462,35 @@ 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 final String TRANSIENT_NAME = "Windows.Transient"; + + public static List getPlatformBackdropNames() { + if (Platform.isSupported(ConditionalFeature.WINDOW_BACKDROP)) { + return List.of(TRANSIENT_NAME); + } + return List.of(); + } + + public static int getBackdropIdentifier(StageBackdrop backdrop) { + if (!Platform.isSupported(ConditionalFeature.WINDOW_BACKDROP)) { + return Window.NO_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 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/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..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 @@ -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); @@ -908,4 +910,12 @@ public String getThemeName() { } public abstract GlassRobot createRobot(); + + 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/GlassScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java index e8ec173b4ff..32f5e064506 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..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 @@ -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, null); 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: @@ -1844,4 +1848,14 @@ public int getMultiClickMaxY() { public GlassRobot createRobot() { return com.sun.glass.ui.Application.GetApplication().createRobot(); } + + @Override + 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 7c3ccdc9b10..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 @@ -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 = null; 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,7 +186,12 @@ private void initPlatformWindow() { windowMask |= Window.DARK_FRAME; } - platformWindow = app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask); + int backdropID = Window.NO_BACKDROP_ID; + if (backdrop != null) { + backdropID = app.getBackdropIdentifier(backdrop); + } + + platformWindow = app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask, backdropID); platformWindow.setResizable(resizable); platformWindow.setFocusable(focusable); @@ -919,4 +927,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 b99c2eb8edb..9f3abc08d09 100644 --- a/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java +++ b/modules/javafx.graphics/src/main/java/javafx/stage/Stage.java @@ -567,6 +567,55 @@ public final Window getOwner() { return owner; } + private StageBackdrop backdrop = null; + + /** + * 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 null + */ + @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; + } + + /** + * 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. @@ -1117,10 +1166,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..589020c3fb5 --- /dev/null +++ b/modules/javafx.graphics/src/main/java/javafx/stage/StageBackdrop.java @@ -0,0 +1,124 @@ +/* + * 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 javafx.stage; + +import javafx.application.ConditionalFeature; +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. The effect is drawn behind the Scene's fill and background. + * + *

+ * + * 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 sealed interface StageBackdrop permits StandardStageBackdrop, PlatformStageBackdrop { + + /** + * Gets the backdrop's name + * + * @return The name of the backdrop. + */ + public String getName(); + + /** + * 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(); + } + + /** + * 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 static List getStandardBackdrops() { + return List.of(WINDOW, PARTIAL); + } + + /** + * 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 getPlatformBackdropNames() { + return Toolkit.getToolkit().getPlatformBackdropNames(); + } + + /** + * 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 name) { + return Toolkit.getToolkit().createPlatformBackdrop(name); + } +} + 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 0460762a67b..5f330c91aee 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 d11fef9d7da..92744a4e532 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,13 @@ #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; +-(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 e18fd841dfe..4863325987c 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,43 @@ #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]; +} + +// 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; + } +} - (void)dealloc { @@ -72,21 +109,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 88fc4f1139b..2f4d50eaf68 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]; @@ -1252,18 +1245,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 519521fe335..473f011cca1 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; @@ -89,6 +90,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 ee41396f082..7252a0ae7da 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" @@ -445,13 +446,17 @@ - (NSColor*)setBackgroundColor:(NSColor *)color } } - +- (void)setJFXView:(GlassView3D*)newView +{ + [hostView setJFXView: newView]; + view = newView; +} @end #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; @@ -591,6 +596,29 @@ 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 (backdropID) { + case com_sun_glass_ui_mac_MacWindow_BackdropID_WINDOW: + [window->hostView setBackdrop: NSVisualEffectMaterialUnderWindowBackground]; + break; + case com_sun_glass_ui_mac_MacWindow_BackdropID_SIDEBAR: + [window->hostView setBackdrop: NSVisualEffectMaterialSidebar]; + break; + case com_sun_glass_ui_mac_MacWindow_BackdropID_MENU: + [window->hostView setBackdrop: NSVisualEffectMaterialMenu]; + break; + case 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]; + // Prevent changes to the background color. + isTransparent = true; + break; + } + window->isDecorated = isTitled || isExtended; window->isExtended = isExtended; window->isTransparent = isTransparent; @@ -610,7 +638,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"); @@ -620,7 +648,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); @@ -736,14 +764,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); } /* @@ -916,6 +944,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 +965,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 +976,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 52a4e6b55d2..a80e593efbc 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassApplication.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassApplication.cpp @@ -507,6 +507,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 a6e1d75a6b7..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 @@ -74,11 +75,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), @@ -338,7 +340,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 }; @@ -1439,6 +1441,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; @@ -1560,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) { @@ -1575,7 +1602,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) { @@ -1615,6 +1642,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, @@ -1639,6 +1670,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 (backdrop) { + case com_sun_glass_ui_win_WinWindow_BackdropID_WINDOW: + pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Window); + break; + case com_sun_glass_ui_win_WinWindow_BackdropID_TABBED: + pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Tabbed); + break; + case com_sun_glass_ui_win_WinWindow_BackdropID_TRANSIENT: + pWindow->EnableBackdrop(GlassWindow::BackdropStyle::Transient); + break; + } } return (jlong)hWnd; @@ -1647,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/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.h b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.h index ff9cdf918ae..a75a4f20a45 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 2a86936fad1..4b5f4eb8389 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.BackBufferFormat = D3DFMT_A8R8G8B8; 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..7164107ffe8 --- /dev/null +++ b/tests/manual/stage/BackdropTest.java @@ -0,0 +1,358 @@ +/* + * 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. + */ + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.application.ColorScheme; +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; +import java.util.Map; +import java.util.HashMap; +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) { + 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 class StageBackdropChoice { + StageBackdropChoice(StageBackdrop backdrop) { + this.backdrop = backdrop; + this.label = (backdrop == null ? "None" : backdrop.getName()); + } + + private String label; + private StageBackdrop backdrop; + + public StageBackdrop getBackdrop() { + return backdrop; + } + + public String toString() { + return label; + } + } + + private List backdrops = new ArrayList<>(); + + void initBackdropList() { + 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)); + }); + } + + 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 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(textColor); + return label; + } + + 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, ObjectProperty textColor) { + var label = newLabel(text, textColor); + 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); + + ObjectProperty textColor = new SimpleObjectProperty<>(Color.BLACK); + + // For creating new stages + var styleLabel = newLabel("Style", textColor); + ChoiceBox stageStyleChoice = new ChoiceBox<>(); + stageStyleChoice.getItems().setAll(StageStyleChoice.values()); + stageStyleChoice.setValue(stageStyle); + + var backdropLabel = newLabel("backdrop", textColor); + ChoiceBox backdropChoice = new ChoiceBox<>(); + backdropChoice.getItems().setAll(backdrops); + 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, 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); + 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); + 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()); + }); + + fillChoice.setValue(FillChoice.TRANSPARENT); + if (Platform.getPreferences().getColorScheme() == ColorScheme.LIGHT) { + schemeChoice.setValue(ColorSchemeChoice.LIGHT); + } else { + 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); + } + + private void showStage(Stage stage, StageStyleChoice style, StageBackdropChoice backdrop) + { + stage.setTitle(style.toString()); + stage.initStyle(style.getStageStyle()); + stage.initBackdrop(backdrop == null ? null : 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) { + initBackdropList(); + var ob = backdrops.stream() + .filter(obj -> obj.getBackdrop() == StageBackdrop.WINDOW) + .findFirst(); + showStage(stage, StageStyleChoice.EXTENDED, ob.orElse(null)); + } +}