Restoring window sizes in JavaFX

When doing usability testing of an alpha version of Dashman, one thing that I was strongly asked was to have the windows remember their sizes when you re-open the application. The need was clear as it was annoying to have the window be a different size when re-started.

The new version of Dashman is built using Java and JavaFX and thus I searched for how to do this, how to restore size. I found many posts, forums, questions, etc all with the same simplistic solution: restoring width and height, and maybe position.

What those were missing was restoring whether the window was maximized (maximized is not the same as occupying all the available space, at least in Windows). But most important than that, none of the solutions took into consideration the fact that the resolutions and quantity of screens could be different than the last time the application run, thus, you could end up with a window completely out of bounds, invisible, immobile.

I came up with this solution, a class that’s designed to be serializable to your config to store the values but also restore them and make sure the window is visible and if not, move it to a visible place:

// Copyright (c) 2017 Flexpoint Tech Ltd. All rights reserved.

package tech.dashman.dashman;

import com.fasterxml.jackson.annotation.JsonIgnore;
import javafx.geometry.Rectangle2D;
import javafx.stage.Screen;
import javafx.stage.Stage;
import lombok.Data;
import tech.dashman.common.Jsonable;

public class StageSizer implements Jsonable {
    private static int MINIMUM_VISIBLE_WIDTH = 100;
    private static int MINIMUM_VISIBLE_HEIGHT = 50;
    private static int MARGIN = 50;

    private Boolean maximized;
    private Number x;
    private Number y;
    private Number width;
    private Number height;

    public void setStage(Stage stage) {
        // First, restore the size and position of the stage.
        // If the stage is not visible in any of the current screens, relocate it the primary screen.
        if (isWindowIsOutOfBounds(stage)) {
        // And now watch the stage to keep the properties updated.

    private void resizeAndPosition(Stage stage) {
        if (getX() != null) {
            stage.setX((Double) getX());
        if (getY() != null) {
            stage.setY((Double) getY());
        if (getWidth() != null) {
            stage.setWidth((Double) getWidth());
        if (getHeight() != null) {
            stage.setHeight((Double) getHeight());
        if (getMaximized() != null) {

    private boolean isWindowIsOutOfBounds(Stage stage) {
        boolean windowIsOutOfBounds = true;
        for (Screen screen : Screen.getScreens()) {
            Rectangle2D bounds = screen.getVisualBounds();
            if (bounds.getMinX() < stage.getX() && stage.getX() + MINIMUM_VISIBLE_WIDTH < bounds.getMaxX() &&
                    bounds.getMinY() < stage.getY() && stage.getY() + MINIMUM_VISIBLE_HEIGHT < bounds.getMaxY()) {
                windowIsOutOfBounds = false;
        return windowIsOutOfBounds;

    private void moveToPrimaryScreen(Stage stage) {
        Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
        stage.setX(bounds.getMinX() + MARGIN);
        stage.setY(bounds.getMinY() + MARGIN);
        stage.setWidth(bounds.getWidth() - MARGIN * 2);
        stage.setHeight(bounds.getHeight() - MARGIN * 2);

    private void watchStage(Stage stage) {
        // Get the current values.
        // Watch for future changes.
        stage.xProperty().addListener((observable, old, x) -> setX(x));
        stage.yProperty().addListener((observable, old, y) -> setY(y));
        stage.widthProperty().addListener((observable, old, width) -> setWidth(width));
        stage.heightProperty().addListener((observable, old, height) -> setHeight(height));
        stage.maximizedProperty().addListener((observable, old, maximized) -> setMaximized(maximized));

and the way you use it is quite simple. On your start method, you create or restore an instance of StageSizer and then do this:

public void start(Stage stage) {
    StageSizer stageSizer = createOrRestoreStageSizerFromConfig();

I haven’t put a lot of testing on this code yet but it seems to work. Well, at least on Windows. The problem is that this snippet is interacting with the reality of screen sizes, resolutions, adding and removing monitors, etc. If you find a bug, please, let me know and I might release this a library with the fix so we can keep on collectively improving this.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s