-
Bug
-
Resolution: Fixed
-
P3
-
8u45
-
x86
-
windows_8
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8139209 | 8u72 | Chien Yang | P3 | Closed | Fixed | b02 |
FULL PRODUCT VERSION :
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.3.9600]
A DESCRIPTION OF THE PROBLEM :
The main problem is that layout() is not called on a Control that is requesting a layout in a certain situation.
In detail, when a control is a child of an unmanaged container which has its layoutflag not set to LayoutFlags.CLEAN (due to the control requesting a layout call) and this container is added to the SceneGraph at a parent which has its layoutflag set to LayoutFlags.CLEAN, the CLEAN flag remains on the parent (instead of being switched to DIRTY_BRANCH) and the requestLayout() calls of the control in the subgraph never get attention.
The effect is that the control is never displayed correctly.
We believe that the bug is in line 265 of class Parent:
if (node.isManaged()) {
relayout = true;
}
should be
if (node.isManaged() || (node instanceof Parent && ((Parent)node).layoutFlag != LayoutFlags.CLEAN)) {
relayout = true;
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Start the test application. Step through with the button "perform next step" repeatedly.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Step 1: A ProgressBar appears. This step is necessary for the ProgressBar to create a skin first.
Step 2: The ProgressBar disappears.
Step 3: The progress property of the ProgressBar is set to a value to invoke a requestLayout call and the container of the ProgressBar becomes dirty. The progress is set to 0.6 which means "a bit more than half full"
Step 4: The ProgressBar appears again. The progress of 0.6 is clearly visible indicated by a bar inside the ProgressBar
Any subsequent step: The progress of the ProgressBar is changed and the change becomes visible immediately.
ACTUAL -
Step 1: A ProgressBar appears. This step is necessary for the ProgressBar to create a skin first.
Step 2: The ProgressBar disappears.
Step 3: The progress property of the ProgressBar is set to a value to invoke a requestLayout call and the container of the ProgressBar becomes dirty. The progress is set to 0.6 which means "a bit more than half full"
Step 4: The ProgressBar appears again. The progress of 0.6 is not visible.
Any subsequent step: The progress of the ProgressBar is changed but the change is not visible.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class UnmanagedParentBugDemo extends Application {
private int step;
private Group intermediate;
Group innerGroup;
Group outerGroup;
@Override
public void start(Stage primaryStage) throws Exception {
ProgressBar progressBar = new ProgressBar(0);
progressBar.setPrefWidth(100);
progressBar.setMinWidth(100);
progressBar.setMinHeight(20);
progressBar.setPrefHeight(20);
progressBar.setLayoutX(100);
progressBar.setLayoutY(100);
progressBar.autosize();
progressBar.layout();
innerGroup = new Group();
innerGroup.setManaged(false);
innerGroup.getChildren().add(progressBar);
outerGroup = new Group();
outerGroup.setManaged(false);
BorderPane borderPane = new BorderPane(outerGroup);
Button button = new Button("Perform Next Step");
button.setOnAction(event -> {
switch(step){
case 0:
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
Innergroup (dirty)
|
|____________Progressbar
*/
if (outerGroup.getChildren().isEmpty()) {
outerGroup.getChildren().add(innerGroup);
}
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|________Innergroup (dirty)
|
|____________Progressbar
*/
step++;
break;
case 1:
outerGroup.getChildren().remove(innerGroup);
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
Innergroup (clean)
|
|____________Progressbar
*/
intermediate = new Group();
intermediate.setManaged(false);
intermediate.setAutoSizeChildren(false);
outerGroup.getChildren().add(intermediate);
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|_________Intermediategroup (dirty)
Innergroup (clean)
|
|____________Progressbar
*/
step++;
break;
case 2:
progressBar.setProgress(0.6); // layoutflag in the Innergroup is set to dirty because of this
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|_________Intermediategroup (dirty)
Innergroup (dirty)
|
|____________Progressbar
*/
step++;
break;
case 3:
intermediate.getChildren().add(innerGroup);
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|_________Intermediategroup (dirty)
|
|___________Innergroup (dirty)
|
|____________Progressbar
*/
step++;
break;
case 4:
progressBar.setProgress(Math.random());
System.out.println("progressBar = " + progressBar.getProgress());
break;
}
System.out.println("step = " + step);
});
borderPane.setTop(button);
primaryStage.setScene(new Scene(borderPane, 500, 500));
primaryStage.show();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
* Call this.requestLayout() every time a child is added to a parent. This is not feasible for production code, obviously.
* Call layout() on the control by yourself.
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.3.9600]
A DESCRIPTION OF THE PROBLEM :
The main problem is that layout() is not called on a Control that is requesting a layout in a certain situation.
In detail, when a control is a child of an unmanaged container which has its layoutflag not set to LayoutFlags.CLEAN (due to the control requesting a layout call) and this container is added to the SceneGraph at a parent which has its layoutflag set to LayoutFlags.CLEAN, the CLEAN flag remains on the parent (instead of being switched to DIRTY_BRANCH) and the requestLayout() calls of the control in the subgraph never get attention.
The effect is that the control is never displayed correctly.
We believe that the bug is in line 265 of class Parent:
if (node.isManaged()) {
relayout = true;
}
should be
if (node.isManaged() || (node instanceof Parent && ((Parent)node).layoutFlag != LayoutFlags.CLEAN)) {
relayout = true;
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Start the test application. Step through with the button "perform next step" repeatedly.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Step 1: A ProgressBar appears. This step is necessary for the ProgressBar to create a skin first.
Step 2: The ProgressBar disappears.
Step 3: The progress property of the ProgressBar is set to a value to invoke a requestLayout call and the container of the ProgressBar becomes dirty. The progress is set to 0.6 which means "a bit more than half full"
Step 4: The ProgressBar appears again. The progress of 0.6 is clearly visible indicated by a bar inside the ProgressBar
Any subsequent step: The progress of the ProgressBar is changed and the change becomes visible immediately.
ACTUAL -
Step 1: A ProgressBar appears. This step is necessary for the ProgressBar to create a skin first.
Step 2: The ProgressBar disappears.
Step 3: The progress property of the ProgressBar is set to a value to invoke a requestLayout call and the container of the ProgressBar becomes dirty. The progress is set to 0.6 which means "a bit more than half full"
Step 4: The ProgressBar appears again. The progress of 0.6 is not visible.
Any subsequent step: The progress of the ProgressBar is changed but the change is not visible.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class UnmanagedParentBugDemo extends Application {
private int step;
private Group intermediate;
Group innerGroup;
Group outerGroup;
@Override
public void start(Stage primaryStage) throws Exception {
ProgressBar progressBar = new ProgressBar(0);
progressBar.setPrefWidth(100);
progressBar.setMinWidth(100);
progressBar.setMinHeight(20);
progressBar.setPrefHeight(20);
progressBar.setLayoutX(100);
progressBar.setLayoutY(100);
progressBar.autosize();
progressBar.layout();
innerGroup = new Group();
innerGroup.setManaged(false);
innerGroup.getChildren().add(progressBar);
outerGroup = new Group();
outerGroup.setManaged(false);
BorderPane borderPane = new BorderPane(outerGroup);
Button button = new Button("Perform Next Step");
button.setOnAction(event -> {
switch(step){
case 0:
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
Innergroup (dirty)
|
|____________Progressbar
*/
if (outerGroup.getChildren().isEmpty()) {
outerGroup.getChildren().add(innerGroup);
}
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|________Innergroup (dirty)
|
|____________Progressbar
*/
step++;
break;
case 1:
outerGroup.getChildren().remove(innerGroup);
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
Innergroup (clean)
|
|____________Progressbar
*/
intermediate = new Group();
intermediate.setManaged(false);
intermediate.setAutoSizeChildren(false);
outerGroup.getChildren().add(intermediate);
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|_________Intermediategroup (dirty)
Innergroup (clean)
|
|____________Progressbar
*/
step++;
break;
case 2:
progressBar.setProgress(0.6); // layoutflag in the Innergroup is set to dirty because of this
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|_________Intermediategroup (dirty)
Innergroup (dirty)
|
|____________Progressbar
*/
step++;
break;
case 3:
intermediate.getChildren().add(innerGroup);
/*
Scene
|
|___BorderPane
|
|_____ Outergroup (clean)
|
|_________Intermediategroup (dirty)
|
|___________Innergroup (dirty)
|
|____________Progressbar
*/
step++;
break;
case 4:
progressBar.setProgress(Math.random());
System.out.println("progressBar = " + progressBar.getProgress());
break;
}
System.out.println("step = " + step);
});
borderPane.setTop(button);
primaryStage.setScene(new Scene(borderPane, 500, 500));
primaryStage.show();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
* Call this.requestLayout() every time a child is added to a parent. This is not feasible for production code, obviously.
* Call layout() on the control by yourself.
- backported by
-
JDK-8139209 Incorrect LayoutFlags adjustment when adding unmanaged children to SceneGraph
-
- Closed
-