Skip to content

Commit 0270847

Browse files
hjohnMartin Fox
andcommitted
8351867: No UI changes while iconified
8146479: Scene is black after stage is restored (content changed while minimized) Co-authored-by: Martin Fox <mfox@openjdk.org> Reviewed-by: lkostyra, angorya
1 parent b9dd4de commit 0270847

File tree

2 files changed

+142
-9
lines changed

2 files changed

+142
-9
lines changed

modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassWindowEventHandler.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@ public Void get() {
5858
case WindowEvent.MAXIMIZE:
5959
stage.stageListener.changedIconified(false);
6060
stage.stageListener.changedMaximized(true);
61+
forceRepaint();
6162
break;
6263
case WindowEvent.RESTORE:
6364
stage.stageListener.changedIconified(false);
6465
stage.stageListener.changedMaximized(false);
66+
forceRepaint();
6567
break;
6668
case WindowEvent.MOVE: {
6769
float wx = window.getX();
@@ -105,15 +107,7 @@ public Void get() {
105107
float outScaleX = window.getOutputScaleX();
106108
float outScaleY = window.getOutputScaleY();
107109
stage.stageListener.changedScale(outScaleX, outScaleY);
108-
// We need to sync the new scales for painting
109-
QuantumToolkit.runWithRenderLock(() -> {
110-
GlassScene scene = stage.getScene();
111-
if (scene != null) {
112-
scene.entireSceneNeedsRepaint();
113-
scene.updateSceneState();
114-
}
115-
return null;
116-
});
110+
forceRepaint();
117111
break;
118112
}
119113
case WindowEvent.FOCUS_GAINED:
@@ -153,6 +147,20 @@ public Void get() {
153147
return null;
154148
}
155149

150+
/**
151+
* Used to trigger a repaint after a window leaves an iconified state.
152+
*/
153+
private void forceRepaint() {
154+
QuantumToolkit.runWithRenderLock(() -> {
155+
GlassScene scene = stage.getScene();
156+
if (scene != null) {
157+
scene.entireSceneNeedsRepaint();
158+
scene.updateSceneState();
159+
}
160+
return null;
161+
});
162+
}
163+
156164
@Override
157165
public void handleLevelEvent(int level) {
158166
QuantumToolkit.runWithoutRenderLock(() -> {
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package test.robot.javafx.stage;
27+
28+
import java.util.concurrent.CountDownLatch;
29+
import java.util.concurrent.TimeUnit;
30+
31+
import javafx.application.Platform;
32+
import javafx.scene.Scene;
33+
import javafx.scene.layout.Pane;
34+
import javafx.scene.paint.Color;
35+
import javafx.stage.Stage;
36+
import javafx.stage.StageStyle;
37+
38+
import org.junit.jupiter.api.Assertions;
39+
import org.junit.jupiter.api.Timeout;
40+
import org.junit.jupiter.params.ParameterizedTest;
41+
import org.junit.jupiter.params.provider.EnumSource;
42+
43+
import static test.util.Util.TIMEOUT;
44+
45+
import test.robot.testharness.VisualTestBase;
46+
47+
/**
48+
* Test that scene changes made while a stage is iconified get drawn after the
49+
* stage is de-iconified.
50+
*
51+
* Note: on macOS you should run these tests with the Desktop & Dock
52+
* "Minimize windows into application icon" setting turned off. When this
53+
* setting is turned on the OS keeps generating new NSScreen objects and the
54+
* resulting notifications will mask the original JavaFX bug.
55+
*/
56+
@Timeout(value=15000, unit=TimeUnit.MILLISECONDS)
57+
public class DrawAfterDeiconifyTest extends VisualTestBase {
58+
59+
private static final int WIDTH = 300;
60+
private static final int HEIGHT = 300;
61+
62+
private static final Color FIRST_COLOR = Color.LIME;
63+
private static final Color SECOND_COLOR = Color.HOTPINK;
64+
65+
private static final double TOLERANCE = 0.07;
66+
67+
private Stage stage;
68+
private int centerX;
69+
private int centerY;
70+
71+
public void redrawsAfterDeiconify(StageStyle stageStyle, final boolean maximized) throws Exception {
72+
final CountDownLatch stageShownLatch = new CountDownLatch(1);
73+
74+
runAndWait(() -> {
75+
stage = getStage(false);
76+
stage.initStyle(stageStyle);
77+
Scene scene = new Scene(new Pane(), WIDTH, HEIGHT);
78+
scene.setFill(FIRST_COLOR);
79+
stage.setScene(scene);
80+
stage.setOnShown(e -> {
81+
Platform.runLater(() -> {
82+
stage.setMaximized(maximized);
83+
centerX = (int)(stage.getX() + stage.getWidth() / 2.0);
84+
centerY = (int)(stage.getY() + stage.getHeight() / 2.0);
85+
stageShownLatch.countDown();
86+
});
87+
});
88+
stage.show();
89+
});
90+
91+
Assertions.assertTrue(stageShownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS), "Timeout waiting for stage to be shown");
92+
93+
waitFirstFrame();
94+
runAndWait(() -> {
95+
Color color = getColor(centerX, centerY);
96+
assertColorEquals(FIRST_COLOR, color, TOLERANCE);
97+
stage.setIconified(true);
98+
});
99+
100+
// Update the scene and then wait for a pulse to clear the pending
101+
// paint request.
102+
runAndWait(() -> stage.getScene().setFill(SECOND_COLOR));
103+
waitNextFrame();
104+
105+
// Deiconify and verify that the scene change gets redrawn
106+
runAndWait(() -> stage.setIconified(false));
107+
waitNextFrame();
108+
runAndWait(() -> {
109+
Color color = getColor(centerX, centerY);
110+
assertColorEquals(SECOND_COLOR, color, TOLERANCE);
111+
});
112+
}
113+
114+
@ParameterizedTest
115+
@EnumSource(names = {"DECORATED", "UNDECORATED", "TRANSPARENT"})
116+
public void stageRedrawsAfterDeiconify(StageStyle stageStyle) throws Exception {
117+
redrawsAfterDeiconify(stageStyle, false);
118+
}
119+
120+
@ParameterizedTest
121+
@EnumSource(names = {"DECORATED", "UNDECORATED", "TRANSPARENT"})
122+
public void maximizedStageRedrawsAfterDeiconify(StageStyle stageStyle) throws Exception {
123+
redrawsAfterDeiconify(stageStyle, true);
124+
}
125+
}

0 commit comments

Comments
 (0)