0

I am working on a new project that will need to show a separate stage on the secondary monitor. This will be a non-interactive stage (only used to display nodes). I will follow this approach to handle that part.

However, I also want to have a duplicate copy of that stage visible within a pane in my main app. It would need to update itself at the same time the stage does.

Where would I start learning how to implement this? Does Java provide a built-in API to display realtime screenshots of a stage, by chance?

Zephyr
  • 9,885
  • 4
  • 28
  • 63

1 Answers1

0

I am no expert but I think there are two ways to do it.

Method 1

Wrap everything of that stage into a main FXML/controller file/class (as a View). Then you need to load that FXML file twice, once in the new stage, the other in a dedicated space you have prepared in your main stage.

The reference of both controller instances should ideally be held at the same object, either in your application class or the class hosting your main stage.

From there, you can either bind values and let the binding API do the work for you.

Example:

Main View:

public class MainView {
    private Model model = new Model();
    private Pane space; // Dedicated space

    public void spawnView() {
        FXMLLoader spawnViewLoader = new FXMLLoader(getClass().getResource("View.fxml"));
        Parent spawnView = spawnViewLoader.load(); // Need to catch IOException
        ViewController spawnController = spawnViewLoader.getController();
        spawnController.setup(model);

        new Stage(new Scene(view));

        FXMLLoader dupViewLoader = new FXMLLoader(getClass().getResource("View.fxml"));
        Parent dupView = dupViewLoader.load(); // Need to catch IOException
        ViewController dupController = dupViewLoader.getController();
        dupController.setup(model);

        space.getChildren().setAll(dupView);
    }
}

Model:

public class Model {
    private final StringProperty title = new SimpleStringProperty();
    private final StringProperty titleProperty() { return title; }
    private final String getTitle() { return title.get(); }
    private final void setTitle(final String title) { this.title.set(title); }
}

View controller:

public class ViewController {
    @FXML private Label label;

    private Model model;

    public void setup(Model model) {
        if (model == null)
            throw new NullPointerException();

        this.model = model;
        label.textProperty().bind(model.titleProperty());
    }

    // Other stuff
}

Be careful of the binding though - I'm quite sure whatever I wrote is going to cause memory leak; both Views will not clean up as long you're holding a reference of model. You can always do it without binding, but it's going to be more tedious to update values.

Method 2

This method is more complex and is likely to be several frames slower. I'm not going to post sample codes as this is not that straight-forward.

You need to have a reference of the Scene of that stage, and use an AnimationTimer to call the snapshot method of the scene object.

Then you need to use an ImageView in your main stage to display the snapshots returned.

Seriously, I think this method would cause the duplicate View to be several frames slower than the original's.

Community
  • 1
  • 1
Jai
  • 8,165
  • 2
  • 21
  • 52