Skip to content

Commit 5398b14

Browse files
author
Michael Strauß
committed
8359601: Fix window button states of an extended stage
8359763: Close request handler is not called for an extended stage Reviewed-by: kcr Backport-of: bc433da
1 parent b60486c commit 5398b14

File tree

20 files changed

+342
-325
lines changed

20 files changed

+342
-325
lines changed

modules/javafx.graphics/src/main/java/com/sun/glass/ui/HeaderButtonOverlay.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
package com.sun.glass.ui;
2727

2828
import com.sun.glass.events.MouseEvent;
29-
import com.sun.javafx.binding.ObjectConstant;
3029
import com.sun.javafx.util.Utils;
3130
import javafx.beans.property.DoubleProperty;
3231
import javafx.beans.property.ObjectProperty;
@@ -45,6 +44,7 @@
4544
import javafx.css.StyleableIntegerProperty;
4645
import javafx.css.StyleableObjectProperty;
4746
import javafx.css.StyleableProperty;
47+
import javafx.event.Event;
4848
import javafx.geometry.Dimension2D;
4949
import javafx.geometry.HPos;
5050
import javafx.geometry.Insets;
@@ -57,6 +57,7 @@
5757
import javafx.scene.paint.Paint;
5858
import javafx.stage.Stage;
5959
import javafx.stage.StageStyle;
60+
import javafx.stage.WindowEvent;
6061
import javafx.util.Subscription;
6162
import java.util.ArrayList;
6263
import java.util.Comparator;
@@ -264,12 +265,15 @@ protected void invalidated() {
264265
private final ButtonRegion maximizeButton = new ButtonRegion(HeaderButtonType.MAXIMIZE, "-FX-INTERNAL-maximize-button", 1);
265266
private final ButtonRegion closeButton = new ButtonRegion(HeaderButtonType.CLOSE, "-FX-INTERNAL-close-button", 2);
266267
private final Subscription subscriptions;
268+
private final boolean modalOrOwned;
267269
private final boolean utility;
268270
private final boolean rightToLeft;
269271

270272
private Node buttonAtMouseDown;
271273

272-
public HeaderButtonOverlay(ObservableValue<String> stylesheet, boolean utility, boolean rightToLeft) {
274+
public HeaderButtonOverlay(ObservableValue<String> stylesheet, boolean modalOrOwned,
275+
boolean utility, boolean rightToLeft) {
276+
this.modalOrOwned = modalOrOwned;
273277
this.utility = utility;
274278
this.rightToLeft = rightToLeft;
275279

@@ -310,10 +314,7 @@ public HeaderButtonOverlay(ObservableValue<String> stylesheet, boolean utility,
310314
getStyleClass().setAll("-FX-INTERNAL-header-button-container");
311315

312316
if (utility) {
313-
iconifyButton.managedProperty().bind(ObjectConstant.valueOf(false));
314-
maximizeButton.managedProperty().bind(ObjectConstant.valueOf(false));
315317
getChildren().add(closeButton);
316-
getStyleClass().add(UTILITY_STYLE_CLASS);
317318
} else {
318319
getChildren().addAll(iconifyButton, maximizeButton, closeButton);
319320
}
@@ -438,7 +439,7 @@ private void handleMouseUp(Node node, HeaderButtonType buttonType) {
438439
switch (buttonType) {
439440
case ICONIFY -> stage.setIconified(true);
440441
case MAXIMIZE -> stage.setMaximized(!stage.isMaximized());
441-
case CLOSE -> stage.close();
442+
case CLOSE -> Event.fireEvent(stage, new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST));
442443
}
443444
}
444445
}
@@ -450,6 +451,9 @@ private void onFocusedChanged(boolean focused) {
450451
}
451452

452453
private void onResizableChanged(boolean resizable) {
454+
boolean utilityStyle = utility || (modalOrOwned && !resizable);
455+
toggleStyleClass(this, UTILITY_STYLE_CLASS, utilityStyle);
456+
iconifyButton.setDisable(utility || modalOrOwned);
453457
maximizeButton.setDisable(!resizable);
454458
}
455459

modules/javafx.graphics/src/main/java/com/sun/glass/ui/Window.java

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ protected Window(Window owner, Screen screen, int styleMask) {
280280
this.styleMask = styleMask;
281281
this.isDecorated = (this.styleMask & Window.TITLED) != 0;
282282
this.isPopup = (this.styleMask & Window.POPUP) != 0;
283+
this.isModal = (this.styleMask & Window.MODAL) != 0;
283284

284285
this.screen = screen != null ? screen : Screen.getMainScreen();
285286
if (PrismSettings.allowHiDPIScaling) {
@@ -1157,43 +1158,6 @@ public void toBack() {
11571158
_toBack(this.ptr);
11581159
}
11591160

1160-
// *****************************************************
1161-
// modality (prototype using native platform feature)
1162-
// *****************************************************
1163-
protected abstract void _enterModal(long ptr);
1164-
/**
1165-
* Enter modal state blocking everything except our window.
1166-
*/
1167-
public void enterModal() {
1168-
checkNotClosed();
1169-
if (this.isModal == false) {
1170-
this.isModal = true;
1171-
_enterModal(this.ptr);
1172-
}
1173-
}
1174-
1175-
protected abstract void _enterModalWithWindow(long dialog, long window);
1176-
/**
1177-
* Enter modal state only blocking the given window.
1178-
* On Mac OS X this is done using a dialog sheet.
1179-
*/
1180-
public void enterModal(final Window window) {
1181-
checkNotClosed();
1182-
if (this.isModal == false) {
1183-
this.isModal = true;
1184-
_enterModalWithWindow(this.ptr, window.getNativeHandle());
1185-
}
1186-
}
1187-
1188-
protected abstract void _exitModal(long ptr);
1189-
public void exitModal() {
1190-
checkNotClosed();
1191-
if (this.isModal == true) {
1192-
_exitModal(this.ptr);
1193-
this.isModal = false;
1194-
}
1195-
}
1196-
11971161
public boolean isModal() {
11981162
return this.isModal;
11991163
}

modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkWindow.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,6 @@ protected boolean _setMenubar(long ptr, long menubarPtr) {
115115
@Override
116116
protected native void _toBack(long ptr);
117117

118-
@Override
119-
protected native void _enterModal(long ptr);
120-
121-
@Override
122-
protected native void _enterModalWithWindow(long dialog, long window);
123-
124-
@Override
125-
protected native void _exitModal(long ptr);
126-
127118
protected native long _getNativeWindowImpl(long ptr);
128119

129120
private native void _showSystemMenu(long ptr, int x, int y);
@@ -255,7 +246,7 @@ private void onPrefHeaderButtonHeightChanged(Number height) {
255246
private HeaderButtonOverlay createHeaderButtonOverlay() {
256247
var overlay = new HeaderButtonOverlay(
257248
PlatformThemeObserver.getInstance().stylesheetProperty(),
258-
isUtilityWindow(),
249+
isModal() || getOwner() != null, isUtilityWindow(),
259250
(getStyleMask() & RIGHT_TO_LEFT) != 0);
260251

261252
// Set the system-defined absolute minimum size to the size of the window buttons area,

modules/javafx.graphics/src/main/java/com/sun/glass/ui/ios/IosWindow.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ protected IosWindow(Window owner, Screen screen, int styleMask) {
6161
@Override native protected void _setIcon(long ptr, Pixels pixels);
6262
@Override native protected void _toFront(long ptr);
6363
@Override native protected void _toBack(long ptr);
64-
@Override native protected void _enterModal(long ptr);
65-
@Override native protected void _enterModalWithWindow(long dialog, long window);
66-
@Override native protected void _exitModal(long ptr);
6764
@Override native protected boolean _grabFocus(long ptr);
6865
@Override native protected void _ungrabFocus(long ptr);
6966

modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacWindow.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,6 @@ protected MacWindow(Window owner, Screen screen, int styleMask) {
115115

116116
@Override native protected void _toFront(long ptr);
117117
@Override native protected void _toBack(long ptr);
118-
@Override native protected void _enterModal(long ptr);
119-
@Override native protected void _enterModalWithWindow(long dialog, long window);
120-
@Override native protected void _exitModal(long ptr);
121118

122119
@Override native protected boolean _grabFocus(long ptr);
123120
@Override native protected void _ungrabFocus(long ptr);

modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -392,28 +392,6 @@ protected void _ungrabFocus(long ptr) {
392392
MonocleWindowManager.getInstance().ungrabFocus(this);
393393
}
394394

395-
/**
396-
* The functions below are used when the platform support modality natively.
397-
* Currently only GTK is using it. This functionality is disabled by
398-
* default. In order to enable it this class need to override Window::
399-
* supportsPlatformModality() to return true.
400-
*
401-
*/
402-
@Override
403-
protected void _enterModal(long ptr) {
404-
throw new UnsupportedOperationException();
405-
}
406-
407-
@Override
408-
protected void _enterModalWithWindow(long dialog, long window) {
409-
throw new UnsupportedOperationException();
410-
}
411-
412-
@Override
413-
protected void _exitModal(long ptr) {
414-
throw new UnsupportedOperationException();
415-
}
416-
417395
@Override
418396
protected void notifyClose() {
419397
super.notifyClose();

modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinHeaderButtonOverlay.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ public class WinHeaderButtonOverlay extends HeaderButtonOverlay {
5656
{ 2.5, 0.95 },
5757
};
5858

59-
public WinHeaderButtonOverlay(boolean utility, boolean rightToLeft) {
60-
super(getStylesheet(), utility, rightToLeft);
59+
public WinHeaderButtonOverlay(boolean modalOrOwned, boolean utility, boolean rightToLeft) {
60+
super(getStylesheet(), modalOrOwned, utility, rightToLeft);
6161

6262
var windowProperty = sceneProperty().flatMap(Scene::windowProperty);
6363

modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinWindow.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,6 @@ protected boolean _setBackground(long ptr, float r, float g, float b) {
289289
@Override native protected void _setIcon(long ptr, Pixels pixels);
290290
@Override native protected void _toFront(long ptr);
291291
@Override native protected void _toBack(long ptr);
292-
@Override native protected void _enterModal(long ptr);
293-
@Override native protected void _enterModalWithWindow(long dialog, long window);
294-
@Override native protected void _exitModal(long ptr);
295292
@Override native protected boolean _grabFocus(long ptr);
296293
@Override native protected void _ungrabFocus(long ptr);
297294
@Override native protected void _setCursor(long ptr, Cursor cursor);
@@ -381,7 +378,7 @@ private void onPrefHeaderButtonHeightChanged(Number height) {
381378
*/
382379
private HeaderButtonOverlay createHeaderButtonOverlay() {
383380
var overlay = new WinHeaderButtonOverlay(
384-
isUtilityWindow(),
381+
isModal() || getOwner() != null, isUtilityWindow(),
385382
(getStyleMask() & RIGHT_TO_LEFT) != 0);
386383

387384
overlay.prefButtonHeightProperty().bind(prefHeaderButtonHeightProperty());

modules/javafx.graphics/src/main/java/com/sun/javafx/scene/layout/HeaderButtonBehavior.java

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,21 @@
2828
import com.sun.javafx.PlatformUtil;
2929
import javafx.beans.value.ObservableValue;
3030
import javafx.css.PseudoClass;
31+
import javafx.event.Event;
3132
import javafx.event.EventHandler;
3233
import javafx.scene.Node;
3334
import javafx.scene.Scene;
35+
import javafx.scene.input.MouseButton;
3436
import javafx.scene.input.MouseEvent;
3537
import javafx.scene.layout.HeaderButtonType;
3638
import javafx.stage.Modality;
3739
import javafx.stage.Stage;
40+
import javafx.stage.StageStyle;
41+
import javafx.stage.WindowEvent;
3842
import javafx.util.Subscription;
3943
import java.util.Objects;
4044

41-
public final class HeaderButtonBehavior implements EventHandler<MouseEvent> {
45+
public final class HeaderButtonBehavior implements EventHandler<Event> {
4246

4347
private static final PseudoClass MAXIMIZED_PSEUDO_CLASS = PseudoClass.getPseudoClass("maximized");
4448

@@ -54,20 +58,24 @@ public HeaderButtonBehavior(Node node, HeaderButtonType type) {
5458
.flatMap(Scene::windowProperty)
5559
.map(w -> w instanceof Stage s ? s : null);
5660

57-
if (type == HeaderButtonType.MAXIMIZE) {
58-
subscription = Subscription.combine(
61+
Subscription subscription = Subscription.combine(
62+
stage.flatMap(Stage::fullScreenProperty).subscribe(this::onFullScreenChanged),
63+
() -> node.removeEventHandler(MouseEvent.MOUSE_RELEASED, this));
64+
65+
if (type != HeaderButtonType.CLOSE) {
66+
subscription = Subscription.combine(subscription,
67+
stage.subscribe(this::onStageChanged),
5968
stage.flatMap(Stage::resizableProperty).subscribe(this::onResizableChanged),
60-
stage.flatMap(Stage::fullScreenProperty).subscribe(this::onFullScreenChanged),
61-
stage.flatMap(Stage::maximizedProperty).subscribe(this::onMaximizedChanged),
62-
() -> node.removeEventHandler(MouseEvent.MOUSE_RELEASED, this)
63-
);
64-
} else {
65-
subscription = Subscription.combine(
66-
stage.flatMap(Stage::fullScreenProperty).subscribe(this::onFullScreenChanged),
67-
() -> node.removeEventHandler(MouseEvent.MOUSE_RELEASED, this)
68-
);
69+
() -> { if (getStage() instanceof Stage s) s.removeEventFilter(WindowEvent.WINDOW_SHOWING, this); });
70+
}
71+
72+
if (type == HeaderButtonType.MAXIMIZE) {
73+
subscription = Subscription.combine(subscription,
74+
stage.flatMap(Stage::maximizedProperty).subscribe(this::onMaximizedChanged));
6975
}
7076

77+
this.subscription = subscription;
78+
7179
node.addEventHandler(MouseEvent.MOUSE_RELEASED, this);
7280

7381
if (!node.focusTraversableProperty().isBound()) {
@@ -80,8 +88,16 @@ public void dispose() {
8088
}
8189

8290
@Override
83-
public void handle(MouseEvent event) {
84-
if (!node.getLayoutBounds().contains(event.getX(), event.getY())) {
91+
public void handle(Event event) {
92+
if (event instanceof MouseEvent mouseEvent) {
93+
handleMouseEvent(mouseEvent);
94+
} else if (event instanceof WindowEvent windowEvent) {
95+
handleWindowEvent(windowEvent);
96+
}
97+
}
98+
99+
private void handleMouseEvent(MouseEvent event) {
100+
if (!node.getLayoutBounds().contains(event.getX(), event.getY()) || event.getButton() != MouseButton.PRIMARY) {
85101
return;
86102
}
87103

@@ -91,7 +107,7 @@ public void handle(MouseEvent event) {
91107
}
92108

93109
switch (type) {
94-
case CLOSE -> stage.close();
110+
case CLOSE -> Event.fireEvent(stage, new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST));
95111
case ICONIFY -> stage.setIconified(true);
96112
case MAXIMIZE -> {
97113
// On macOS, a non-modal window is put into full-screen mode when the maximize button is clicked,
@@ -105,6 +121,12 @@ public void handle(MouseEvent event) {
105121
}
106122
}
107123

124+
private void handleWindowEvent(WindowEvent event) {
125+
if (event.getEventType() == WindowEvent.WINDOW_SHOWING && getStage() instanceof Stage stage) {
126+
updateButtonDisableState(stage);
127+
}
128+
}
129+
108130
private Stage getStage() {
109131
Scene scene = node.getScene();
110132
if (scene == null) {
@@ -114,9 +136,32 @@ private Stage getStage() {
114136
return scene.getWindow() instanceof Stage stage ? stage : null;
115137
}
116138

117-
private void onResizableChanged(Boolean resizable) {
139+
private void onStageChanged(Stage oldStage, Stage newStage) {
140+
if (oldStage != null) {
141+
oldStage.removeEventFilter(WindowEvent.WINDOW_SHOWING, this);
142+
}
143+
144+
if (newStage != null) {
145+
newStage.addEventFilter(WindowEvent.WINDOW_SHOWING, this);
146+
}
147+
}
148+
149+
private void onResizableChanged(Boolean unused) {
150+
if (getStage() instanceof Stage stage) {
151+
updateButtonDisableState(stage);
152+
}
153+
}
154+
155+
private void updateButtonDisableState(Stage stage) {
118156
if (!node.disableProperty().isBound()) {
119-
node.setDisable(resizable == Boolean.FALSE);
157+
boolean utility = stage.getStyle() == StageStyle.UTILITY;
158+
boolean modalOrOwned = stage.getOwner() != null || stage.getModality() != Modality.NONE;
159+
boolean resizable = stage.isResizable();
160+
161+
switch (type) {
162+
case ICONIFY -> node.setDisable(utility || modalOrOwned);
163+
case MAXIMIZE -> node.setDisable(!resizable);
164+
}
120165
}
121166
}
122167

0 commit comments

Comments
 (0)