From 975d6ef96784f3c495bec220fe061c997176485d Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Sun, 15 Jun 2025 20:25:47 +0200 Subject: [PATCH 1/8] Remove unused window modality code --- .../main/java/com/sun/glass/ui/Window.java | 38 +---------- .../java/com/sun/glass/ui/gtk/GtkWindow.java | 9 --- .../java/com/sun/glass/ui/ios/IosWindow.java | 3 - .../java/com/sun/glass/ui/mac/MacWindow.java | 3 - .../sun/glass/ui/monocle/MonocleWindow.java | 22 ------- .../java/com/sun/glass/ui/win/WinWindow.java | 3 - .../src/main/native-glass/gtk/GlassWindow.cpp | 46 -------------- .../src/main/native-glass/ios/GlassWindow.m | 54 ---------------- .../src/main/native-glass/mac/GlassWindow.m | 63 ------------------- 9 files changed, 1 insertion(+), 240 deletions(-) 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 4a9df6eb404..5912de3bb72 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 @@ -280,6 +280,7 @@ protected Window(Window owner, Screen screen, int styleMask) { 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.screen = screen != null ? screen : Screen.getMainScreen(); if (PrismSettings.allowHiDPIScaling) { @@ -1157,43 +1158,6 @@ public void toBack() { _toBack(this.ptr); } - // ***************************************************** - // modality (prototype using native platform feature) - // ***************************************************** - protected abstract void _enterModal(long ptr); - /** - * Enter modal state blocking everything except our window. - */ - public void enterModal() { - checkNotClosed(); - if (this.isModal == false) { - this.isModal = true; - _enterModal(this.ptr); - } - } - - protected abstract void _enterModalWithWindow(long dialog, long window); - /** - * Enter modal state only blocking the given window. - * On Mac OS X this is done using a dialog sheet. - */ - public void enterModal(final Window window) { - checkNotClosed(); - if (this.isModal == false) { - this.isModal = true; - _enterModalWithWindow(this.ptr, window.getNativeHandle()); - } - } - - protected abstract void _exitModal(long ptr); - public void exitModal() { - checkNotClosed(); - if (this.isModal == true) { - _exitModal(this.ptr); - this.isModal = false; - } - } - public boolean isModal() { return this.isModal; } 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 4bc3e1b1f39..be56bacef57 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 @@ -115,15 +115,6 @@ protected boolean _setMenubar(long ptr, long menubarPtr) { @Override protected native void _toBack(long ptr); - @Override - protected native void _enterModal(long ptr); - - @Override - protected native void _enterModalWithWindow(long dialog, long window); - - @Override - protected native void _exitModal(long ptr); - protected native long _getNativeWindowImpl(long ptr); private native void _showSystemMenu(long ptr, int x, int y); 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 12fd200fb7f..3dc6116163e 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 @@ -61,9 +61,6 @@ protected IosWindow(Window owner, Screen screen, int styleMask) { @Override native protected void _setIcon(long ptr, Pixels pixels); @Override native protected void _toFront(long ptr); @Override native protected void _toBack(long ptr); - @Override native protected void _enterModal(long ptr); - @Override native protected void _enterModalWithWindow(long dialog, long window); - @Override native protected void _exitModal(long ptr); @Override native protected boolean _grabFocus(long ptr); @Override native protected void _ungrabFocus(long ptr); 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 49adc4c0f61..af1c55851cb 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 @@ -115,9 +115,6 @@ protected MacWindow(Window owner, Screen screen, int styleMask) { @Override native protected void _toFront(long ptr); @Override native protected void _toBack(long ptr); - @Override native protected void _enterModal(long ptr); - @Override native protected void _enterModalWithWindow(long dialog, long window); - @Override native protected void _exitModal(long ptr); @Override native protected boolean _grabFocus(long ptr); @Override native protected void _ungrabFocus(long ptr); 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 fba8a19be2e..063b301d61a 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 @@ -392,28 +392,6 @@ protected void _ungrabFocus(long ptr) { MonocleWindowManager.getInstance().ungrabFocus(this); } - /** - * The functions below are used when the platform support modality natively. - * Currently only GTK is using it. This functionality is disabled by - * default. In order to enable it this class need to override Window:: - * supportsPlatformModality() to return true. - * - */ - @Override - protected void _enterModal(long ptr) { - throw new UnsupportedOperationException(); - } - - @Override - protected void _enterModalWithWindow(long dialog, long window) { - throw new UnsupportedOperationException(); - } - - @Override - protected void _exitModal(long ptr) { - throw new UnsupportedOperationException(); - } - @Override protected void notifyClose() { super.notifyClose(); 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 7a48e6e82c1..8c9e1d6160a 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 @@ -289,9 +289,6 @@ protected boolean _setBackground(long ptr, float r, float g, float b) { @Override native protected void _setIcon(long ptr, Pixels pixels); @Override native protected void _toFront(long ptr); @Override native protected void _toBack(long ptr); - @Override native protected void _enterModal(long ptr); - @Override native protected void _enterModalWithWindow(long dialog, long window); - @Override native protected void _exitModal(long ptr); @Override native protected boolean _grabFocus(long ptr); @Override native protected void _ungrabFocus(long ptr); @Override native protected void _setCursor(long ptr, Cursor cursor); 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 16f033357ed..5707060bb08 100644 --- a/modules/javafx.graphics/src/main/native-glass/gtk/GlassWindow.cpp +++ b/modules/javafx.graphics/src/main/native-glass/gtk/GlassWindow.cpp @@ -466,52 +466,6 @@ JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1toBack ctx->to_back(); } -/* - * Class: com_sun_glass_ui_gtk_GtkWindow - * Method: _enterModal - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1enterModal - (JNIEnv * env, jobject obj, jlong ptr) -{ - (void)env; - (void)obj; - - WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); - ctx->set_modal(true); -} - -/* - * Class: com_sun_glass_ui_gtk_GtkWindow - * Method: _enterModalWithWindow - * Signature: (JJ)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1enterModalWithWindow - (JNIEnv * env, jobject obj, jlong ptrDialog, jlong ptrWindow) -{ - (void)env; - (void)obj; - - WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptrDialog); - WindowContext* parent_ctx = JLONG_TO_WINDOW_CTX(ptrWindow); - ctx->set_modal(true, parent_ctx); -} - -/* - * Class: com_sun_glass_ui_gtk_GtkWindow - * Method: _exitModal - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkWindow__1exitModal - (JNIEnv * env, jobject obj, jlong ptr) -{ - (void)env; - (void)obj; - - WindowContext* ctx = JLONG_TO_WINDOW_CTX(ptr); - ctx->set_modal(false); -} - /* * Class: com_sun_glass_ui_gtk_GtkCursor * Method: _setCursorType 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 d2409d77d60..4d3940a94dc 100644 --- a/modules/javafx.graphics/src/main/native-glass/ios/GlassWindow.m +++ b/modules/javafx.graphics/src/main/native-glass/ios/GlassWindow.m @@ -1587,60 +1587,6 @@ - (void) _createWindow } -/* - * Class: com_sun_glass_ui_ios_IosWindow - * Method: _enterModal - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_ios_IosWindow__1enterModal -(JNIEnv *env, jobject jwindow, jlong windowPtr) { - GLASS_LOG("Java_com_sun_glass_ui_ios_IosWindow__1enterModal called."); - GLASS_ASSERT_MAIN_JAVA_THREAD(env); - GLASS_POOL_ENTER; - { - // implementation omes here - } - GLASS_POOL_EXIT; - GLASS_CHECK_EXCEPTION(env); -} - - -/* - * Class: com_sun_glass_ui_ios_IosWindow - * Method: _enterModalWithWindow - * Signature: (JJ)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_ios_IosWindow__1enterModalWithWindow -(JNIEnv *env, jobject jwindow, jlong windowPtr, jlong window) { - GLASS_LOG("Java_com_sun_glass_ui_ios_IosWindow__1enterModalWithWindow called."); - GLASS_ASSERT_MAIN_JAVA_THREAD(env); - GLASS_POOL_ENTER; - { - // implemenation comes here - } - GLASS_POOL_EXIT; - GLASS_CHECK_EXCEPTION(env); -} - - -/* - * Class: com_sun_glass_ui_ios_IosWindow - * Method: _exitModal - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_ios_IosWindow__1exitModal -(JNIEnv *env, jobject jwindow, jlong windowPtr) { - GLASS_LOG("Java_com_sun_glass_ui_ios_IosWindow__1exitModal called."); - GLASS_ASSERT_MAIN_JAVA_THREAD(env); - GLASS_POOL_ENTER; - { - // implementation comes here - } - GLASS_POOL_EXIT; - GLASS_CHECK_EXCEPTION(env); -} - - /* * Class: com_sun_glass_ui_ios_IosWindow * Method: _setEnabled 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 e52b653dbee..285452201ab 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m @@ -1404,69 +1404,6 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr GLASS_CHECK_EXCEPTION(env); } - -/* - * Class: com_sun_glass_ui_mac_MacWindow - * Method: _enterModal - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1enterModal -(JNIEnv *env, jobject jWindow, jlong jPtr) -{ - LOG("Java_com_sun_glass_ui_mac_MacWindow__1enterModal"); - if (!jPtr) return; - - GLASS_ASSERT_MAIN_JAVA_THREAD(env); - GLASS_POOL_ENTER; - { - GlassWindow *window = getGlassWindow(env, jPtr); - [NSApp runModalForWindow:window->nsWindow]; - } - GLASS_POOL_EXIT; - GLASS_CHECK_EXCEPTION(env); -} - -/* - * Class: com_sun_glass_ui_mac_MacWindow - * Method: _enterModalWithWindow - * Signature: (JJ)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1enterModalWithWindow -(JNIEnv *env, jobject jWindow, jlong jDialogPtr, jlong jWindowPtr) -{ - LOG("Java_com_sun_glass_ui_mac_MacWindow__1enterModalWithWindow"); - - GLASS_ASSERT_MAIN_JAVA_THREAD(env); - GLASS_POOL_ENTER; - { - //GlassWindow *window = getGlassWindow(env, jDialogPtr); - // TODO: implement _enterModalWithWindow - } - GLASS_POOL_EXIT; - GLASS_CHECK_EXCEPTION(env); -} - -/* - * Class: com_sun_glass_ui_mac_MacWindow - * Method: _exitModal - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacWindow__1exitModal -(JNIEnv *env, jobject jWindow, jlong jPtr) -{ - LOG("Java_com_sun_glass_ui_mac_MacWindow__1exitModal"); - if (!jPtr) return; - - GLASS_ASSERT_MAIN_JAVA_THREAD(env); - GLASS_POOL_ENTER; - { - GlassWindow *window = getGlassWindow(env, jPtr); - [NSApp stop:window->nsWindow]; - } - GLASS_POOL_EXIT; - GLASS_CHECK_EXCEPTION(env); -} - /* * Class: com_sun_glass_ui_mac_MacWindow * Method: _performWindowDrag From 63fcaa6dd4b4d48f40a50a0082c0d85b6d11361a Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Sun, 15 Jun 2025 20:28:36 +0200 Subject: [PATCH 2/8] Fix disabled/hidden states of default window buttons --- .../com/sun/glass/ui/HeaderButtonOverlay.java | 12 +-- .../java/com/sun/glass/ui/gtk/GtkWindow.java | 3 +- .../glass/ui/win/WinHeaderButtonOverlay.java | 4 +- .../java/com/sun/glass/ui/win/WinWindow.java | 2 +- .../src/main/native-glass/win/GlassWindow.cpp | 2 +- .../glass/ui/gtk/WindowDecorationGnome.css | 12 +-- .../sun/glass/ui/gtk/WindowDecorationKDE.css | 5 +- .../com/sun/glass/ui/win/WindowDecoration.css | 9 ++ .../sun/glass/ui/HeaderButtonOverlayTest.java | 82 +++++++++++++++---- 9 files changed, 97 insertions(+), 34 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java index afbb0d8bd53..3ccedfdbb74 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java @@ -26,7 +26,6 @@ package com.sun.glass.ui; import com.sun.glass.events.MouseEvent; -import com.sun.javafx.binding.ObjectConstant; import com.sun.javafx.util.Utils; import javafx.beans.property.DoubleProperty; import javafx.beans.property.ObjectProperty; @@ -264,12 +263,15 @@ protected void invalidated() { private final ButtonRegion maximizeButton = new ButtonRegion(HeaderButtonType.MAXIMIZE, "-FX-INTERNAL-maximize-button", 1); private final ButtonRegion closeButton = new ButtonRegion(HeaderButtonType.CLOSE, "-FX-INTERNAL-close-button", 2); private final Subscription subscriptions; + private final boolean modal; private final boolean utility; private final boolean rightToLeft; private Node buttonAtMouseDown; - public HeaderButtonOverlay(ObservableValue stylesheet, boolean utility, boolean rightToLeft) { + public HeaderButtonOverlay(ObservableValue stylesheet, boolean modal, + boolean utility, boolean rightToLeft) { + this.modal = modal; this.utility = utility; this.rightToLeft = rightToLeft; @@ -310,10 +312,7 @@ public HeaderButtonOverlay(ObservableValue stylesheet, boolean utility, getStyleClass().setAll("-FX-INTERNAL-header-button-container"); if (utility) { - iconifyButton.managedProperty().bind(ObjectConstant.valueOf(false)); - maximizeButton.managedProperty().bind(ObjectConstant.valueOf(false)); getChildren().add(closeButton); - getStyleClass().add(UTILITY_STYLE_CLASS); } else { getChildren().addAll(iconifyButton, maximizeButton, closeButton); } @@ -450,6 +449,9 @@ private void onFocusedChanged(boolean focused) { } private void onResizableChanged(boolean resizable) { + boolean utilityStyle = utility || (modal && !resizable); + toggleStyleClass(this, UTILITY_STYLE_CLASS, utilityStyle); + iconifyButton.setDisable(utility || modal); maximizeButton.setDisable(!resizable); } 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 be56bacef57..ad16b3f1ab0 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 @@ -246,8 +246,7 @@ private void onPrefHeaderButtonHeightChanged(Number height) { private HeaderButtonOverlay createHeaderButtonOverlay() { var overlay = new HeaderButtonOverlay( PlatformThemeObserver.getInstance().stylesheetProperty(), - isUtilityWindow(), - (getStyleMask() & RIGHT_TO_LEFT) != 0); + isModal(), isUtilityWindow(), (getStyleMask() & RIGHT_TO_LEFT) != 0); // Set the system-defined absolute minimum size to the size of the window buttons area, // regardless of whether the application has specified a smaller minimum size. diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java index 95a557ddd55..b2c3be6babc 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java @@ -56,8 +56,8 @@ public class WinHeaderButtonOverlay extends HeaderButtonOverlay { { 2.5, 0.95 }, }; - public WinHeaderButtonOverlay(boolean utility, boolean rightToLeft) { - super(getStylesheet(), utility, rightToLeft); + public WinHeaderButtonOverlay(boolean modal, boolean utility, boolean rightToLeft) { + super(getStylesheet(), modal, utility, rightToLeft); var windowProperty = sceneProperty().flatMap(Scene::windowProperty); 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 8c9e1d6160a..a07f1f8be0a 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 @@ -378,7 +378,7 @@ private void onPrefHeaderButtonHeightChanged(Number height) { */ private HeaderButtonOverlay createHeaderButtonOverlay() { var overlay = new WinHeaderButtonOverlay( - isUtilityWindow(), + isModal(), isUtilityWindow(), (getStyleMask() & RIGHT_TO_LEFT) != 0); overlay.prefButtonHeightProperty().bind(prefHeaderButtonHeightProperty()); 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 74216836442..5d8b2739b0c 100644 --- a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp +++ b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp @@ -924,7 +924,7 @@ BOOL GlassWindow::HandleNCHitTestEvent(SHORT x, SHORT y, LRESULT& result) int topBorderHeight = ::GetSystemMetrics(SM_CXPADDEDBORDER) + ::GetSystemMetrics(SM_CYSIZEFRAME); RECT windowRect; - if (::GetWindowRect(GetHWND(), &windowRect) && y < windowRect.top + topBorderHeight) { + if (m_isResizable && ::GetWindowRect(GetHWND(), &windowRect) && y < windowRect.top + topBorderHeight) { result = LRESULT(HTTOP); return TRUE; } diff --git a/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationGnome.css b/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationGnome.css index af7372979b3..188025f66a3 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationGnome.css +++ b/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationGnome.css @@ -101,12 +101,12 @@ -fx-background-color: white; } -.-FX-INTERNAL-maximize-button:disabled { - -fx-background-color: transparent; -} - -.-FX-INTERNAL-maximize-button:disabled > .-FX-INTERNAL-glyph { - -fx-background-color: #777; +.-FX-INTERNAL-iconify-button:disabled, +.-FX-INTERNAL-maximize-button:disabled, +.-FX-INTERNAL-header-button-container.utility > .-FX-INTERNAL-iconify-button, +.-FX-INTERNAL-header-button-container.utility > .-FX-INTERNAL-maximize-button { + -fx-managed: false; + visibility: hidden; } .-FX-INTERNAL-iconify-button > .-FX-INTERNAL-glyph { diff --git a/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationKDE.css b/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationKDE.css index 3ab8e232ee5..e2cb64f6de1 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationKDE.css +++ b/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/gtk/WindowDecorationKDE.css @@ -89,7 +89,10 @@ -fx-background-color: #6c7076 !important; } -.-FX-INTERNAL-maximize-button:disabled { +.-FX-INTERNAL-iconify-button:disabled, +.-FX-INTERNAL-maximize-button:disabled, +.-FX-INTERNAL-header-button-container.utility > .-FX-INTERNAL-iconify-button, +.-FX-INTERNAL-header-button-container.utility > .-FX-INTERNAL-maximize-button { -fx-managed: false; visibility: hidden; } diff --git a/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/win/WindowDecoration.css b/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/win/WindowDecoration.css index 80f7288960c..21371382aee 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/win/WindowDecoration.css +++ b/modules/javafx.graphics/src/main/resources/com/sun/glass/ui/win/WindowDecoration.css @@ -87,14 +87,23 @@ -fx-background-color: white; } +.-FX-INTERNAL-header-button-container.utility > .-FX-INTERNAL-iconify-button, +.-FX-INTERNAL-header-button-container.utility > .-FX-INTERNAL-maximize-button { + -fx-managed: false; + visibility: hidden; +} + +.-FX-INTERNAL-iconify-button:disabled, .-FX-INTERNAL-maximize-button:disabled { -fx-background-color: transparent !important; } +.-FX-INTERNAL-iconify-button:disabled > .-FX-INTERNAL-glyph, .-FX-INTERNAL-maximize-button:disabled > .-FX-INTERNAL-glyph { -fx-background-color: #00000020 !important; } +.-FX-INTERNAL-iconify-button.dark:disabled > .-FX-INTERNAL-glyph, .-FX-INTERNAL-maximize-button.dark:disabled > .-FX-INTERNAL-glyph { -fx-background-color: #ffffff20 !important; } diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java index 87b0d0185c6..a4b49c4df68 100644 --- a/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java +++ b/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java @@ -36,6 +36,8 @@ import javafx.scene.paint.Color; import javafx.stage.Stage; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import test.util.ReflectionUtils; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -57,7 +59,7 @@ void rightPlacement_stretchAlignment() { -fx-button-default-height: 20; -fx-button-vertical-alignment: stretch; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; } - """), false, false); + """), false, false, false); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -83,7 +85,7 @@ void rightPlacement_stretchAlignment_rightToLeft() { -fx-button-default-height: 20; -fx-button-vertical-alignment: stretch; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; } - """), false, true); + """), false, false, true); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -111,7 +113,7 @@ void rightPlacement_centerAlignment() { -fx-button-default-height: 20; -fx-button-vertical-alignment: center; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, false); + """), false, false, false); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -138,7 +140,7 @@ void rightPlacement_centerAlignment_rightToLeft() { -fx-button-default-height: 20; -fx-button-vertical-alignment: center; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, true); + """), false, false, true); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -165,7 +167,7 @@ void leftPlacement_stretchAlignment() { -fx-button-default-height: 20; -fx-button-vertical-alignment: stretch; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; } - """), false, false); + """), false, false, false); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -191,7 +193,7 @@ void leftPlacement_stretchAlignment_rightToLeft() { -fx-button-default-height: 20; -fx-button-vertical-alignment: stretch; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; } - """), false, true); + """), false, false, true); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -219,7 +221,7 @@ void leftPlacement_centerAlignment() { -fx-button-default-height: 20; -fx-button-vertical-alignment: center; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, false); + """), false, false, false); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -246,7 +248,7 @@ void leftPlacement_centerAlignment_rightToLeft() { -fx-button-default-height: 20; -fx-button-vertical-alignment: center; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, true); + """), false, false, true); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -274,7 +276,7 @@ void customButtonOrder() { .-FX-INTERNAL-iconify-button { -fx-button-order: 5; } .-FX-INTERNAL-maximize-button { -fx-button-order: 1; } .-FX-INTERNAL-close-button { -fx-button-order: 3; } - """), false, false); + """), false, false, false); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -301,7 +303,7 @@ void customButtonOrder_rightToLeft() { .-FX-INTERNAL-iconify-button { -fx-button-order: 5; } .-FX-INTERNAL-maximize-button { -fx-button-order: 1; } .-FX-INTERNAL-close-button { -fx-button-order: 3; } - """), false, true); + """), false, false, true); var unused = new Scene(overlay); var children = overlay.getChildrenUnmodifiable(); @@ -322,19 +324,67 @@ void customButtonOrder_rightToLeft() { void utilityDecorationIsOnlyCloseButton() { var overlay = new HeaderButtonOverlay(getStylesheet(""" .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), true, false); + """), false, true, false); var children = overlay.getChildrenUnmodifiable(); assertEquals(1, children.size()); assertTrue(children.getFirst().getStyleClass().contains("-FX-INTERNAL-close-button")); } + enum ButtonDisabledStateTest { + RESIZABLE(true, false, false, false), + UNRESIZABLE(false, false, false, true), + MODAL_RESIZABLE(true, true, true, false), + MODAL_UNRESIZABLE(false, true, true, true); + + ButtonDisabledStateTest(boolean resizable, boolean modal, boolean iconifyDisabled, boolean maximizeDisabled) { + this.resizable = resizable; + this.modal = modal; + this.iconifyDisabled = iconifyDisabled; + this.maximizeDisabled = maximizeDisabled; + } + + final boolean resizable; + final boolean modal; + final boolean iconifyDisabled; + final boolean maximizeDisabled; + } + + /** + * Tests the disabled states of the iconify and maximize buttons for all combinations + * of resizable and modal window attributes. + */ + @ParameterizedTest + @EnumSource(ButtonDisabledStateTest.class) + void buttonDisabledStateIsCorrect(ButtonDisabledStateTest test) { + var overlay = new HeaderButtonOverlay(getStylesheet(""" + .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } + """), test.modal, false, false); + + var scene = new Scene(overlay); + var stage = new Stage(); + stage.setScene(scene); + stage.setResizable(test.resizable); + + var children = overlay.getChildrenUnmodifiable(); + assertEquals(3, children.size()); + + assertTrue(children.get(0).getStyleClass().contains("-FX-INTERNAL-iconify-button")); + assertEquals(test.iconifyDisabled, children.get(0).isDisabled()); + + assertTrue(children.get(1).getStyleClass().contains("-FX-INTERNAL-maximize-button")); + assertEquals(test.maximizeDisabled, children.get(1).isDisabled()); + + assertTrue(children.get(2).getStyleClass().contains("-FX-INTERNAL-close-button")); + assertFalse(children.get(2).isDisabled()); + } + @Test void activePseudoClassCorrespondsToStageFocusedProperty() { var overlay = new HeaderButtonOverlay(getStylesheet(""" .-FX-INTERNAL-header-button-container { -fx-button-placement: right; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, false); + """), false, false, false); var scene = new Scene(overlay); var stage = new Stage(); @@ -360,7 +410,7 @@ void maximizeButtonIsDisabledWhenStageIsNotResizable() { var overlay = new HeaderButtonOverlay(getStylesheet(""" .-FX-INTERNAL-header-button-container { -fx-button-placement: right; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, false); + """), false, false, false); var scene = new Scene(overlay); var stage = new Stage(); @@ -384,7 +434,7 @@ void restoreStyleClassIsPresentWhenStageIsMaximized() { var overlay = new HeaderButtonOverlay(getStylesheet(""" .-FX-INTERNAL-header-button-container { -fx-button-placement: right; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, false); + """), false, false, false); var scene = new Scene(overlay); var stage = new Stage(); @@ -407,7 +457,7 @@ void darkStyleClassIsPresentWhenSceneFillIsDark() { var overlay = new HeaderButtonOverlay(getStylesheet(""" .-FX-INTERNAL-header-button-container { -fx-button-placement: right; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, false); + """), false, false, false); var scene = new Scene(overlay); @@ -426,7 +476,7 @@ void pickButtonAtCoordinates() { var overlay = new HeaderButtonOverlay(getStylesheet(""" .-FX-INTERNAL-header-button-container { -fx-button-placement: right; -fx-button-vertical-alignment: stretch; } .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), false, false); + """), false, false, false); var unused = new Scene(overlay); overlay.resize(200, 100); From b3d7ae624dadbd4e102f125b56112f13928978cd Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Sun, 15 Jun 2025 21:22:42 +0200 Subject: [PATCH 3/8] Fix disabled states of custom window buttons --- .../scene/layout/HeaderButtonBehavior.java | 48 +++++++---- .../layout/HeaderButtonBehaviorTest.java | 83 +++++++++++++++++++ 2 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java index 31689e48d07..d69378778a3 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java @@ -31,10 +31,12 @@ import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Scene; +import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.layout.HeaderButtonType; import javafx.stage.Modality; import javafx.stage.Stage; +import javafx.stage.StageStyle; import javafx.util.Subscription; import java.util.Objects; @@ -54,20 +56,23 @@ public HeaderButtonBehavior(Node node, HeaderButtonType type) { .flatMap(Scene::windowProperty) .map(w -> w instanceof Stage s ? s : null); + Subscription subscription = Subscription.combine( + stage.flatMap(Stage::fullScreenProperty).subscribe(this::onFullScreenChanged), + () -> node.removeEventHandler(MouseEvent.MOUSE_RELEASED, this)); + + if (type != HeaderButtonType.CLOSE) { + subscription = Subscription.combine(subscription, + stage.subscribe(this::onStageChanged), + stage.flatMap(Stage::resizableProperty).subscribe(this::onResizableChanged)); + } + if (type == HeaderButtonType.MAXIMIZE) { - subscription = Subscription.combine( - stage.flatMap(Stage::resizableProperty).subscribe(this::onResizableChanged), - stage.flatMap(Stage::fullScreenProperty).subscribe(this::onFullScreenChanged), - stage.flatMap(Stage::maximizedProperty).subscribe(this::onMaximizedChanged), - () -> node.removeEventHandler(MouseEvent.MOUSE_RELEASED, this) - ); - } else { - subscription = Subscription.combine( - stage.flatMap(Stage::fullScreenProperty).subscribe(this::onFullScreenChanged), - () -> node.removeEventHandler(MouseEvent.MOUSE_RELEASED, this) - ); + subscription = Subscription.combine(subscription, + stage.flatMap(Stage::maximizedProperty).subscribe(this::onMaximizedChanged)); } + this.subscription = subscription; + node.addEventHandler(MouseEvent.MOUSE_RELEASED, this); if (!node.focusTraversableProperty().isBound()) { @@ -81,7 +86,7 @@ public void dispose() { @Override public void handle(MouseEvent event) { - if (!node.getLayoutBounds().contains(event.getX(), event.getY())) { + if (!node.getLayoutBounds().contains(event.getX(), event.getY()) || event.getButton() != MouseButton.PRIMARY) { return; } @@ -114,9 +119,24 @@ private Stage getStage() { return scene.getWindow() instanceof Stage stage ? stage : null; } - private void onResizableChanged(Boolean resizable) { + private void onStageChanged(Stage stage) { + if (stage != null) { + boolean utility = stage.getStyle() == StageStyle.UTILITY; + boolean modal = stage.getModality() != Modality.NONE; + updateDisableState(utility, modal, stage.isResizable()); + } + } + + private void onResizableChanged(Boolean unused) { + onStageChanged(getStage()); + } + + private void updateDisableState(boolean utility, boolean modal, boolean resizable) { if (!node.disableProperty().isBound()) { - node.setDisable(resizable == Boolean.FALSE); + switch (type) { + case ICONIFY -> node.setDisable(utility || modal); + case MAXIMIZE -> node.setDisable(!resizable); + } } } diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java new file mode 100644 index 00000000000..e6d6b3da1d4 --- /dev/null +++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java @@ -0,0 +1,83 @@ +/* + * 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 test.com.sun.javafx.scene.layout; + +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.layout.HeaderBar; +import javafx.scene.layout.HeaderButtonType; +import javafx.stage.Modality; +import javafx.stage.Stage; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.*; + +public class HeaderButtonBehaviorTest { + + enum ButtonDisabledStateTest { + RESIZABLE(true, false, false, false), + UNRESIZABLE(false, false, false, true), + MODAL_RESIZABLE(true, true, true, false), + MODAL_UNRESIZABLE(false, true, true, true); + + ButtonDisabledStateTest(boolean resizable, boolean modal, boolean iconifyDisabled, boolean maximizeDisabled) { + this.resizable = resizable; + this.modal = modal; + this.iconifyDisabled = iconifyDisabled; + this.maximizeDisabled = maximizeDisabled; + } + + final boolean resizable; + final boolean modal; + final boolean iconifyDisabled; + final boolean maximizeDisabled; + } + + /** + * Tests the disabled states of the iconify and maximize buttons for all combinations + * of resizable and modal window attributes. + */ + @ParameterizedTest + @EnumSource(ButtonDisabledStateTest.class) + void buttonDisabledStateIsCorrect(ButtonDisabledStateTest test) { + Node iconify = new Group(), maximize = new Group(), close = new Group(); + HeaderBar.setButtonType(iconify, HeaderButtonType.ICONIFY); + HeaderBar.setButtonType(maximize, HeaderButtonType.MAXIMIZE); + HeaderBar.setButtonType(close, HeaderButtonType.CLOSE); + + var stage = new Stage(); + stage.initModality(test.modal ? Modality.WINDOW_MODAL : Modality.NONE); + stage.setResizable(test.resizable); + stage.setScene(new Scene(new Group(iconify, maximize, close))); + stage.show(); + + assertEquals(test.iconifyDisabled, iconify.isDisabled()); + assertEquals(test.maximizeDisabled, maximize.isDisabled()); + assertFalse(close.isDisabled()); + } +} From 06a0a5bb2947e4226796e400053f6bcb18e98578 Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Tue, 17 Jun 2025 02:20:56 +0200 Subject: [PATCH 4/8] Handle non-modal owned window correctly --- .../java/com/sun/glass/ui/gtk/GtkWindow.java | 3 +- .../java/com/sun/glass/ui/win/WinWindow.java | 2 +- .../scene/layout/HeaderButtonBehavior.java | 46 +++++++++++++++---- .../layout/HeaderButtonBehaviorTest.java | 26 ++++++++--- 4 files changed, 59 insertions(+), 18 deletions(-) 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 ad16b3f1ab0..0fa69994c5f 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 @@ -246,7 +246,8 @@ private void onPrefHeaderButtonHeightChanged(Number height) { private HeaderButtonOverlay createHeaderButtonOverlay() { var overlay = new HeaderButtonOverlay( PlatformThemeObserver.getInstance().stylesheetProperty(), - isModal(), isUtilityWindow(), (getStyleMask() & RIGHT_TO_LEFT) != 0); + isModal() || getOwner() != null, isUtilityWindow(), + (getStyleMask() & RIGHT_TO_LEFT) != 0); // Set the system-defined absolute minimum size to the size of the window buttons area, // regardless of whether the application has specified a smaller minimum size. 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 a07f1f8be0a..c6a21bb5c33 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 @@ -378,7 +378,7 @@ private void onPrefHeaderButtonHeightChanged(Number height) { */ private HeaderButtonOverlay createHeaderButtonOverlay() { var overlay = new WinHeaderButtonOverlay( - isModal(), isUtilityWindow(), + isModal() || getOwner() != null, isUtilityWindow(), (getStyleMask() & RIGHT_TO_LEFT) != 0); overlay.prefButtonHeightProperty().bind(prefHeaderButtonHeightProperty()); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java index d69378778a3..04a7dc35686 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java @@ -28,6 +28,7 @@ import com.sun.javafx.PlatformUtil; import javafx.beans.value.ObservableValue; import javafx.css.PseudoClass; +import javafx.event.Event; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Scene; @@ -37,10 +38,11 @@ import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; +import javafx.stage.WindowEvent; import javafx.util.Subscription; import java.util.Objects; -public final class HeaderButtonBehavior implements EventHandler { +public final class HeaderButtonBehavior implements EventHandler { private static final PseudoClass MAXIMIZED_PSEUDO_CLASS = PseudoClass.getPseudoClass("maximized"); @@ -82,10 +84,22 @@ public HeaderButtonBehavior(Node node, HeaderButtonType type) { public void dispose() { subscription.unsubscribe(); + + if (getStage() instanceof Stage stage) { + stage.removeEventFilter(WindowEvent.WINDOW_SHOWING, this); + } } @Override - public void handle(MouseEvent event) { + public void handle(Event event) { + if (event instanceof MouseEvent mouseEvent) { + handleMouseEvent(mouseEvent); + } else if (event instanceof WindowEvent windowEvent) { + handleWindowEvent(windowEvent); + } + } + + private void handleMouseEvent(MouseEvent event) { if (!node.getLayoutBounds().contains(event.getX(), event.getY()) || event.getButton() != MouseButton.PRIMARY) { return; } @@ -110,6 +124,12 @@ public void handle(MouseEvent event) { } } + private void handleWindowEvent(WindowEvent event) { + if (event.getEventType() == WindowEvent.WINDOW_SHOWING && getStage() instanceof Stage stage) { + updateButtonDisableState(stage); + } + } + private Stage getStage() { Scene scene = node.getScene(); if (scene == null) { @@ -119,20 +139,28 @@ private Stage getStage() { return scene.getWindow() instanceof Stage stage ? stage : null; } - private void onStageChanged(Stage stage) { - if (stage != null) { - boolean utility = stage.getStyle() == StageStyle.UTILITY; - boolean modal = stage.getModality() != Modality.NONE; - updateDisableState(utility, modal, stage.isResizable()); + private void onStageChanged(Stage oldStage, Stage newStage) { + if (oldStage != null) { + oldStage.removeEventFilter(WindowEvent.WINDOW_SHOWING, this); + } + + if (newStage != null) { + newStage.addEventFilter(WindowEvent.WINDOW_SHOWING, this); } } private void onResizableChanged(Boolean unused) { - onStageChanged(getStage()); + if (getStage() instanceof Stage stage) { + updateButtonDisableState(stage); + } } - private void updateDisableState(boolean utility, boolean modal, boolean resizable) { + private void updateButtonDisableState(Stage stage) { if (!node.disableProperty().isBound()) { + boolean utility = stage.getStyle() == StageStyle.UTILITY; + boolean modal = stage.getOwner() != null || stage.getModality() != Modality.NONE; + boolean resizable = stage.isResizable(); + switch (type) { case ICONIFY -> node.setDisable(utility || modal); case MAXIMIZE -> node.setDisable(!resizable); diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java index e6d6b3da1d4..66a2adc4ef1 100644 --- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java +++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java @@ -40,27 +40,34 @@ public class HeaderButtonBehaviorTest { enum ButtonDisabledStateTest { - RESIZABLE(true, false, false, false), - UNRESIZABLE(false, false, false, true), - MODAL_RESIZABLE(true, true, true, false), - MODAL_UNRESIZABLE(false, true, true, true); + RESIZABLE(true, false, false, false, false), + UNRESIZABLE(false, false, false, false, true), + MODAL_RESIZABLE(true, true, false, true, false), + MODAL_UNRESIZABLE(false, true, false, true, true), + OWNER_RESIZABLE(true, false, true, true, false), + OWNER_UNRESIZABLE(false, false, true, true, true), + OWNER_MODAL_RESIZABLE(true, true, true, true, false), + OWNER_MODAL_UNRESIZABLE(false, true, true, true, true); - ButtonDisabledStateTest(boolean resizable, boolean modal, boolean iconifyDisabled, boolean maximizeDisabled) { + ButtonDisabledStateTest(boolean resizable, boolean modal, boolean hasOwner, + boolean iconifyDisabled, boolean maximizeDisabled) { this.resizable = resizable; this.modal = modal; + this.hasOwner = hasOwner; this.iconifyDisabled = iconifyDisabled; this.maximizeDisabled = maximizeDisabled; } final boolean resizable; final boolean modal; + final boolean hasOwner; final boolean iconifyDisabled; final boolean maximizeDisabled; } /** * Tests the disabled states of the iconify and maximize buttons for all combinations - * of resizable and modal window attributes. + * of resizable, modal, and owner window attributes. */ @ParameterizedTest @EnumSource(ButtonDisabledStateTest.class) @@ -74,10 +81,15 @@ void buttonDisabledStateIsCorrect(ButtonDisabledStateTest test) { stage.initModality(test.modal ? Modality.WINDOW_MODAL : Modality.NONE); stage.setResizable(test.resizable); stage.setScene(new Scene(new Group(iconify, maximize, close))); - stage.show(); + if (test.hasOwner) { + stage.initOwner(new Stage()); + } + + stage.show(); assertEquals(test.iconifyDisabled, iconify.isDisabled()); assertEquals(test.maximizeDisabled, maximize.isDisabled()); assertFalse(close.isDisabled()); + stage.close(); } } From ef05139eb3443e951c7274a7e033a0ec3d87f964 Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Tue, 17 Jun 2025 02:38:24 +0200 Subject: [PATCH 5/8] small refactor --- .../com/sun/javafx/scene/layout/HeaderButtonBehavior.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java index 04a7dc35686..127a400c179 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java @@ -65,7 +65,8 @@ public HeaderButtonBehavior(Node node, HeaderButtonType type) { if (type != HeaderButtonType.CLOSE) { subscription = Subscription.combine(subscription, stage.subscribe(this::onStageChanged), - stage.flatMap(Stage::resizableProperty).subscribe(this::onResizableChanged)); + stage.flatMap(Stage::resizableProperty).subscribe(this::onResizableChanged), + () -> { if (getStage() instanceof Stage s) s.removeEventFilter(WindowEvent.WINDOW_SHOWING, this); }); } if (type == HeaderButtonType.MAXIMIZE) { @@ -84,10 +85,6 @@ public HeaderButtonBehavior(Node node, HeaderButtonType type) { public void dispose() { subscription.unsubscribe(); - - if (getStage() instanceof Stage stage) { - stage.removeEventFilter(WindowEvent.WINDOW_SHOWING, this); - } } @Override From dcc21f7685d1a793e5fb69671d4817c3eb1bc9a7 Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Sat, 21 Jun 2025 19:53:30 +0200 Subject: [PATCH 6/8] rename modal to modalOrOwned --- .../java/com/sun/glass/ui/HeaderButtonOverlay.java | 10 +++++----- .../com/sun/glass/ui/win/WinHeaderButtonOverlay.java | 4 ++-- .../sun/javafx/scene/layout/HeaderButtonBehavior.java | 4 ++-- .../test/com/sun/glass/ui/HeaderButtonOverlayTest.java | 9 +++++---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java index 3ccedfdbb74..19e7e4c5c69 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java @@ -263,15 +263,15 @@ protected void invalidated() { private final ButtonRegion maximizeButton = new ButtonRegion(HeaderButtonType.MAXIMIZE, "-FX-INTERNAL-maximize-button", 1); private final ButtonRegion closeButton = new ButtonRegion(HeaderButtonType.CLOSE, "-FX-INTERNAL-close-button", 2); private final Subscription subscriptions; - private final boolean modal; + private final boolean modalOrOwned; private final boolean utility; private final boolean rightToLeft; private Node buttonAtMouseDown; - public HeaderButtonOverlay(ObservableValue stylesheet, boolean modal, + public HeaderButtonOverlay(ObservableValue stylesheet, boolean modalOrOwned, boolean utility, boolean rightToLeft) { - this.modal = modal; + this.modalOrOwned = modalOrOwned; this.utility = utility; this.rightToLeft = rightToLeft; @@ -449,9 +449,9 @@ private void onFocusedChanged(boolean focused) { } private void onResizableChanged(boolean resizable) { - boolean utilityStyle = utility || (modal && !resizable); + boolean utilityStyle = utility || (modalOrOwned && !resizable); toggleStyleClass(this, UTILITY_STYLE_CLASS, utilityStyle); - iconifyButton.setDisable(utility || modal); + iconifyButton.setDisable(utility || modalOrOwned); maximizeButton.setDisable(!resizable); } diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java index b2c3be6babc..f356089c652 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java @@ -56,8 +56,8 @@ public class WinHeaderButtonOverlay extends HeaderButtonOverlay { { 2.5, 0.95 }, }; - public WinHeaderButtonOverlay(boolean modal, boolean utility, boolean rightToLeft) { - super(getStylesheet(), modal, utility, rightToLeft); + public WinHeaderButtonOverlay(boolean modalOrOwned, boolean utility, boolean rightToLeft) { + super(getStylesheet(), modalOrOwned, utility, rightToLeft); var windowProperty = sceneProperty().flatMap(Scene::windowProperty); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java index 127a400c179..02fc4a8b335 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java @@ -155,11 +155,11 @@ private void onResizableChanged(Boolean unused) { private void updateButtonDisableState(Stage stage) { if (!node.disableProperty().isBound()) { boolean utility = stage.getStyle() == StageStyle.UTILITY; - boolean modal = stage.getOwner() != null || stage.getModality() != Modality.NONE; + boolean modalOrOwned = stage.getOwner() != null || stage.getModality() != Modality.NONE; boolean resizable = stage.isResizable(); switch (type) { - case ICONIFY -> node.setDisable(utility || modal); + case ICONIFY -> node.setDisable(utility || modalOrOwned); case MAXIMIZE -> node.setDisable(!resizable); } } diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java index a4b49c4df68..8b934baa531 100644 --- a/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java +++ b/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java @@ -337,15 +337,16 @@ enum ButtonDisabledStateTest { MODAL_RESIZABLE(true, true, true, false), MODAL_UNRESIZABLE(false, true, true, true); - ButtonDisabledStateTest(boolean resizable, boolean modal, boolean iconifyDisabled, boolean maximizeDisabled) { + ButtonDisabledStateTest(boolean resizable, boolean modalOrOwned, + boolean iconifyDisabled, boolean maximizeDisabled) { this.resizable = resizable; - this.modal = modal; + this.modalOrOwned = modalOrOwned; this.iconifyDisabled = iconifyDisabled; this.maximizeDisabled = maximizeDisabled; } final boolean resizable; - final boolean modal; + final boolean modalOrOwned; final boolean iconifyDisabled; final boolean maximizeDisabled; } @@ -359,7 +360,7 @@ enum ButtonDisabledStateTest { void buttonDisabledStateIsCorrect(ButtonDisabledStateTest test) { var overlay = new HeaderButtonOverlay(getStylesheet(""" .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } - """), test.modal, false, false); + """), test.modalOrOwned, false, false); var scene = new Scene(overlay); var stage = new Stage(); From 20eac39e38974b3d80291df157b0d52706312ad0 Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:52:18 +0200 Subject: [PATCH 7/8] fix window close request event --- .../com/sun/glass/ui/HeaderButtonOverlay.java | 4 ++- .../scene/layout/HeaderButtonBehavior.java | 2 +- .../sun/glass/ui/HeaderButtonOverlayTest.java | 26 +++++++++++++++++ .../layout/HeaderButtonBehaviorTest.java | 29 +++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java index 19e7e4c5c69..e022afb1ca9 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java @@ -44,6 +44,7 @@ import javafx.css.StyleableIntegerProperty; import javafx.css.StyleableObjectProperty; import javafx.css.StyleableProperty; +import javafx.event.Event; import javafx.geometry.Dimension2D; import javafx.geometry.HPos; import javafx.geometry.Insets; @@ -56,6 +57,7 @@ import javafx.scene.paint.Paint; import javafx.stage.Stage; import javafx.stage.StageStyle; +import javafx.stage.WindowEvent; import javafx.util.Subscription; import java.util.ArrayList; import java.util.Comparator; @@ -437,7 +439,7 @@ private void handleMouseUp(Node node, HeaderButtonType buttonType) { switch (buttonType) { case ICONIFY -> stage.setIconified(true); case MAXIMIZE -> stage.setMaximized(!stage.isMaximized()); - case CLOSE -> stage.close(); + case CLOSE -> Event.fireEvent(stage, new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST)); } } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java index 02fc4a8b335..7a70734cc65 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java @@ -107,7 +107,7 @@ private void handleMouseEvent(MouseEvent event) { } switch (type) { - case CLOSE -> stage.close(); + case CLOSE -> Event.fireEvent(stage, new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST)); case ICONIFY -> stage.setIconified(true); case MAXIMIZE -> { // On macOS, a non-modal window is put into full-screen mode when the maximize button is clicked, diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java index 8b934baa531..06a8be9dcdc 100644 --- a/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java +++ b/modules/javafx.graphics/src/test/java/test/com/sun/glass/ui/HeaderButtonOverlayTest.java @@ -25,6 +25,7 @@ package test.com.sun.glass.ui; +import com.sun.glass.events.MouseEvent; import com.sun.glass.ui.HeaderButtonOverlay; import com.sun.javafx.binding.ObjectConstant; import javafx.beans.value.ObservableValue; @@ -35,6 +36,7 @@ import javafx.scene.layout.HeaderButtonType; import javafx.scene.paint.Color; import javafx.stage.Stage; +import javafx.stage.WindowEvent; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -490,6 +492,30 @@ void pickButtonAtCoordinates() { assertEquals(HeaderButtonType.CLOSE, overlay.buttonAt(181, 10)); } + /** + * Tests that clicking the close button fires the {@link WindowEvent#WINDOW_CLOSE_REQUEST} event. + */ + @Test + void closeButtonFiresWindowCloseRequestEvent() { + var overlay = new HeaderButtonOverlay(getStylesheet(""" + .-FX-INTERNAL-header-button-container { -fx-button-placement: right; -fx-button-vertical-alignment: stretch; } + .-FX-INTERNAL-header-button { -fx-pref-width: 20; -fx-pref-height: 10; } + """), false, false, false); + + var stage = new Stage(); + var scene = new Scene(overlay); + stage.setScene(scene); + overlay.resize(200, 100); + overlay.applyCss(); + overlay.layout(); + + var flag = new boolean[1]; + stage.setOnCloseRequest(_ -> flag[0] = true); + overlay.handleMouseEvent(MouseEvent.DOWN, MouseEvent.BUTTON_LEFT, 181, 10); + overlay.handleMouseEvent(MouseEvent.UP, MouseEvent.BUTTON_LEFT, 181, 10); + assertTrue(flag[0]); + } + private static ObservableValue getStylesheet(String text) { String stylesheet = "data:text/css;base64," + Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)); diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java index 66a2adc4ef1..a2f86bd6f17 100644 --- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java +++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/scene/layout/HeaderButtonBehaviorTest.java @@ -25,13 +25,19 @@ package test.com.sun.javafx.scene.layout; +import javafx.event.Event; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.HeaderBar; import javafx.scene.layout.HeaderButtonType; +import javafx.scene.layout.Region; import javafx.stage.Modality; import javafx.stage.Stage; +import javafx.stage.WindowEvent; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -92,4 +98,27 @@ void buttonDisabledStateIsCorrect(ButtonDisabledStateTest test) { assertFalse(close.isDisabled()); stage.close(); } + + /** + * Tests that clicking the close button fires the {@link WindowEvent#WINDOW_CLOSE_REQUEST} event. + */ + @Test + void closeButtonFiresWindowCloseRequestEvent() { + var stage = new Stage(); + var button = new Region(); + button.resize(10, 10); + HeaderBar.setButtonType(button, HeaderButtonType.CLOSE); + stage.setScene(new Scene(button)); + stage.show(); + + boolean[] flag = new boolean[1]; + stage.setOnCloseRequest(_ -> flag[0] = true); + + Event.fireEvent(button, new MouseEvent( + MouseEvent.MOUSE_RELEASED, 0, 0, 0, 0, MouseButton.PRIMARY, 1, + false, false, false, false, false, false, false, false, false, false, null)); + + assertTrue(flag[0]); + stage.close(); + } } From cd550b4ba55617e42136524dc9f6ac31c7b927d3 Mon Sep 17 00:00:00 2001 From: mstr2 <43553916+mstr2@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:33:52 +0200 Subject: [PATCH 8/8] fix resizable state of macOS window --- .../main/native-glass/mac/GlassWindow+Java.h | 4 +- .../main/native-glass/mac/GlassWindow+Java.m | 46 +++++++++---------- .../src/main/native-glass/mac/GlassWindow.m | 12 ++--- 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.h b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.h index 98c3908ad41..e449339043f 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -40,7 +40,7 @@ + (void)_resetGrab; - (void)_checkUngrab; - (void)_grabFocus; -- (void)_setResizable; +- (void)_setResizable:(bool)resizable; - (NSRect)_constrainFrame:(NSRect)frame; - (void)_setVisible; - (void)_setBounds:(jint)x y:(jint)y xSet:(jboolean)xSet ySet:(jboolean)ySet w:(jint)w h:(jint)h cw:(jint)cw ch:(jint)ch; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.m b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.m index e6ac4d8b70a..0ebec04d3cc 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow+Java.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -212,34 +212,30 @@ - (void)_grabFocus s_grabWindow = window; } -- (void)_setResizable +- (void)_setResizable:(bool)resizable { - NSUInteger mask = [self->nsWindow styleMask]; - if ((mask & NSWindowStyleMaskResizable) != 0) - { - if (self->isDecorated == YES) - { - mask &= ~(NSUInteger)NSWindowStyleMaskResizable; - [self->nsWindow setStyleMask: mask]; - [self->nsWindow setShowsResizeIndicator:NO]; + self->isResizable = resizable; - NSButton *zoomButton = [self->nsWindow standardWindowButton:NSWindowZoomButton]; - [zoomButton setEnabled:NO]; - } - self->isResizable = NO; + if (self->isDecorated == NO) { + return; } - else - { - if (self->isDecorated == YES) - { - mask |= NSWindowStyleMaskResizable; - [self->nsWindow setStyleMask: mask]; - [self->nsWindow setShowsResizeIndicator:YES]; - NSButton *zoomButton = [self->nsWindow standardWindowButton:NSWindowZoomButton]; - [zoomButton setEnabled:YES]; - } - self->isResizable = YES; + NSUInteger mask = [self->nsWindow styleMask]; + + if (resizable) { + mask |= NSWindowStyleMaskResizable; + [self->nsWindow setStyleMask: mask]; + [self->nsWindow setShowsResizeIndicator:YES]; + + NSButton *zoomButton = [self->nsWindow standardWindowButton:NSWindowZoomButton]; + [zoomButton setEnabled:YES]; + } else { + mask &= ~(NSUInteger)NSWindowStyleMaskResizable; + [self->nsWindow setStyleMask: mask]; + [self->nsWindow setShowsResizeIndicator:NO]; + + NSButton *zoomButton = [self->nsWindow standardWindowButton:NSWindowZoomButton]; + [zoomButton setEnabled:NO]; } } 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 285452201ab..04714c76270 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassWindow.m @@ -471,8 +471,7 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr { window->owner = getGlassWindow(env, jOwnerPtr)->nsWindow; // not retained (use weak reference?) } - window->isResizable = NO; - window->isDecorated = isTitled || isExtended; + /* 10.7 full screen window support */ if ([NSWindow instancesRespondToSelector:@selector(toggleFullScreen:)]) { NSWindowCollectionBehavior behavior = [window->nsWindow collectionBehavior]; @@ -502,10 +501,12 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr [window->nsWindow setOpaque:YES]; } + window->isDecorated = isTitled || isExtended; window->isTransparent = isTransparent; window->isSizeAssigned = NO; window->isLocationAssigned = NO; - + window->isResizable = NO; + [window _setResizable:NO]; // actual value will be set later with a separate JNI downcall } [pool drain]; @@ -1161,10 +1162,7 @@ static jlong _createWindowCommonDo(JNIEnv *env, jobject jWindow, jlong jOwnerPtr GLASS_POOL_ENTER; { GlassWindow *window = getGlassWindow(env, jPtr); - if (window->isResizable != jResizable) - { - [window performSelectorOnMainThread:@selector(_setResizable) withObject:nil waitUntilDone:YES]; - } + [window _setResizable:jResizable]; } GLASS_POOL_EXIT; GLASS_CHECK_EXCEPTION(env);