When a ProgressBar
is indeterminate it has an animation that goes back and forth. This works fine when the ProgressBar
is part of a normal Stage
but doesn't work when part of a Dialog
. Instead, it seems to just sit at the start of the animation. The ProgressBar
does, however, update properly when set to some determinate value. Note: The issue does not appear in Java 8.
There are no indications of any exceptions.
Here's an MCVE (GIF of it in action):
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
Button detButton = new Button("Launch Determinate Task");
detButton.setOnAction(ae -> {
ae.consume();
createDialog(primaryStage, true)
.showAndWait();
});
Button indetButton = new Button("Launch Indeterminate Task");
indetButton.setOnAction(ae -> {
ae.consume();
createDialog(primaryStage, false)
.showAndWait();
});
HBox btnBox = new HBox(detButton, indetButton);
btnBox.setSpacing(10);
btnBox.setAlignment(Pos.CENTER);
StackPane root = new StackPane(btnBox, createDummyProgressNode());
Scene scene = new Scene(root, 500, 300);
primaryStage.setScene(scene);
primaryStage.setTitle("ProgressBar Issue");
primaryStage.setResizable(false);
primaryStage.show();
}
private Node createDummyProgressNode() {
Label label = new Label("ProgressBar to show animation in Stage.");
ProgressBar progressBar = new ProgressBar();
progressBar.setMaxWidth(Double.MAX_VALUE);
VBox box = new VBox(label, progressBar);
box.setMaxHeight(VBox.USE_PREF_SIZE);
box.setSpacing(3);
box.setAlignment(Pos.CENTER_LEFT);
box.setPadding(new Insets(5));
StackPane.setAlignment(box, Pos.BOTTOM_CENTER);
return box;
}
private Dialog<?> createDialog(Stage owner, boolean determinate) {
Task<?> task = new BackgroundTask(determinate);
Dialog<?> dialog = new Dialog<>();
dialog.initOwner(owner);
dialog.setTitle("Background Task - "
+ (determinate ? "Determinate" : "Indeterminate"));
dialog.getDialogPane().setPrefWidth(300);
dialog.getDialogPane().setContent(createDialogContent(task));
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty().bind(task.runningProperty());
dialog.setOnShown(de -> {
de.consume();
executeTask(task);
});
return dialog;
}
private Node createDialogContent(Task<?> task) {
Label label = new Label();
label.textProperty().bind(task.messageProperty());
ProgressBar progressBar = new ProgressBar();
progressBar.setMaxWidth(Double.MAX_VALUE);
progressBar.progressProperty().bind(task.progressProperty());
VBox box = new VBox(label, progressBar);
box.setSpacing(3);
box.setAlignment(Pos.CENTER_LEFT);
return box;
}
private void executeTask(Task<?> task) {
Thread thread = new Thread(task, "background-thread");
thread.setDaemon(true);
thread.start();
}
private static class BackgroundTask extends Task<Void> {
private final boolean determinate;
private BackgroundTask(boolean determinate) {
this.determinate = determinate;
}
@Override
protected Void call() throws Exception {
final int loops = 1_000;
for (int i = 0; i <= loops; i++) {
updateMessage("Running... " + i);
Thread.sleep(1L);
if (determinate) {
updateProgress(i, loops);
}
}
updateMessage("Complete");
updateProgress(loops, loops);
return null;
}
}
}
I tried this code on:
All my tests were on a Windows 10 Home (version 1803) computer.
I'm 90% certain this is some type of regression bug, but just in case...
I've tried things like changing the Modality
and owner
of the Dialog
to no effect. It also seems to only be the ProgressBar
as the Dialog
can still be moved around and the message updates (i.e. the UI is not freezing).
I haven't tried this with other animated Node
s such as ProgressIndicator
.
P.S. I searched the Java bug database for this or similar issue but came up with nothing; I could have easily missed one, however.
Update: I have submitted my own bug report.
The bug report can be found here: JDK-8207837.
9
, 10
, and openjfx-11
.openjfx-12
.Turns out the Dialog
wasn't the root of the problem. The issue is caused by adding the ProgressBar
to a Scene
after the Scene
was already added to a Stage
; the reason this causes a problem is because of JDK-8216377. See this comment for more a better explanation. And here is the attached test code demonstrating the issue:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class IndeterminateProgressBar extends Application {
@Override
public void start(Stage stage) {
StackPane root = new StackPane();
Scene scene = new Scene(root, 300, 150);
stage.setScene(scene);
// If the progress bar is added to the root after the root is
// added to the scene and the scene is added to the stage, it will
// fail to be shown.
ProgressBar progBar = new ProgressBar();
progBar.setProgress(-1);
root.getChildren().add(progBar);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
If you move stage.setScene(scene)
to after root.getChildren().add(progBar)
the ProgressBar
will animate.
Based on this information, I believe a workaround when using a Dialog
is impossible. A Dialog
has an internal Stage
that it uses for display. When a DialogPane
is set on the Dialog
it causes the creation of a Scene
which is then added to this Stage
. Unfortunately, a Dialog
is initialized with a DialogPane
and so no matter what the Scene
will have been added to a Stage
before one can add the ProgressBar
.
To avoid the issue use JavaFX 8 or OpenJFX 12+. It's possible the fix is (or will be) backported to OpenJFX 11 but I'm not sure (please leave a comment if you know one way or the other).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With