Popup intended to display ProgressBar not showing in JavaFX

Viewed 312

I want to display a ProgressBar using a Popup in JavaFX so that I will be able to make the ProgressBar disappear by calling the PopUp's hide() method. However, rather than disappearing when the background task is complete, the ProgessBar doesn't show at all.

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Popup;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage stage) {
        StackPane root = new StackPane();

        Popup popup = new Popup();
        ProgressBar bar = new ProgressBar();
        popup.getContent().add(bar);

        Task<Void> task = new Task<>() {
            @Override
            protected Void call() {
                for (int i = 0; i < 100; i++) {
                    try {
                        Thread.sleep(100);
                        updateProgress(i, 100);
                    } catch (Exception ignored) {
                    }
                }
                return null;
            }
        };

        bar.progressProperty().bind(task.progressProperty());
        task.setOnSucceeded(workerStateEvent -> popup.hide());
        popup.show(stage);
        new Thread(task).start();

        stage.setScene(new Scene(root, 100, 100));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

When I run the above code, all I see is a blank stage, shown here: Blank stage in place of ProgressBar

If I add the ProgressBar to the Stage manually using root.getChildren().add(bar), the bar displays correctly, with its progressProperty properly bonded, so I know that the issue is with the Popup. However, using that method, the bar doesn't disappear when the background task is complete.

1 Answers

I can't find this documented anywhere, but I think the owner of a Popup must be showing for the Popup.show(owner) method to succeed. So the following reordering of your code (calling stage.show() before popup.show(stage)) works:

@Override
public void start(Stage stage) {
    StackPane root = new StackPane();

    Popup popup = new Popup();
    ProgressBar bar = new ProgressBar();
    popup.getContent().add(bar);

    Task<Void> task = new Task<>() {
        @Override
        protected Void call() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                    updateProgress(i, 100);
                } catch (Exception ignored) {
                }
            }
            return null;
        }
    };

    bar.progressProperty().bind(task.progressProperty());
    task.setOnSucceeded(workerStateEvent -> popup.hide());

    stage.setScene(new Scene(root, 100, 100));
    stage.show();
    
    popup.show(stage);
    new Thread(task).start();

}

Note your other approach works: you just have to remove the progress bar from the scene graph when the task is complete:

    @Override
    public void start(Stage stage) {
        StackPane root = new StackPane();

//        Popup popup = new Popup();
        
        ProgressBar bar = new ProgressBar();
//        popup.getContent().add(bar);
        root.getChildren().add(bar);

        Task<Void> task = new Task<>() {
            @Override
            protected Void call() {
                for (int i = 0; i < 100; i++) {
                    try {
                        Thread.sleep(100);
                        updateProgress(i, 100);
                    } catch (Exception ignored) {
                    }
                }
                return null;
            }
        };

        bar.progressProperty().bind(task.progressProperty());
//        task.setOnSucceeded(workerStateEvent -> popup.hide());
        task.setOnSucceeded(event -> root.getChildren().remove(bar));

        stage.setScene(new Scene(root, 100, 100));
        stage.show();

        
//        popup.show(stage);
        new Thread(task).start();

    }
Related