1

I was attempting to layout a JavaFX stage using a GridPane when I ran into the following problem. If I setup the grid with the appropriate constraints and add newly instantiated StackPanes to it, the default sizing of the scene, stage, and it's contents ensures that the contents are visible:

Without CSS

However, if I add a JavaFX CSS style specifying a border to the newly instantiated StackPane before adding it to the GridPane, then the default sizing of things seems to collapse complete:

With CSS

My code is as follows:

public static void main(final String[] args) {
    Platform.startup(() -> {});
    Platform.runLater(() -> {

        final GridPane gridPane = new GridPane();
        final Scene scene = new Scene(gridPane);
        final Stage stage = new Stage();
        stage.setScene(scene);

        final List<StackPane> panes = new ArrayList<>();
        for (int i = 0; i < 4; i++) {

            // Create a new pane with a random background color for
            // illustration
            final StackPane p = createNewPane();
            panes.add(p);

            // The addition / removal of the following line affects the
            // layout.
            p.setStyle("-fx-border-width:2px;-fx-border-color:red");
        }

        for (int r = 0; r < 2; r++) {
            final RowConstraints rc = new RowConstraints();
            rc.setPercentHeight(50);
            gridPane.getRowConstraints().add(rc);
        }
        for (int c = 0; c < 2; c++) {
            final ColumnConstraints cc = new ColumnConstraints();
            cc.setPercentWidth(50);
            gridPane.getColumnConstraints().add(cc);
        }

        for (int r = 0, i = 0; r < 2; r++) {
            for (int c = 0; c < 2; c++) {
                gridPane.add(panes.get(i++), c, r);
            }
        }
        stage.show();
    });
}

Curiously, if I move the stage.show() to right after I set the Scene, then everything works fine even with the CSS.

Can anyone help me understand, one, whether this is the expected behavior, and two, why the execution order of the stage.show() makes a difference?

Thanks!

fabian
  • 80,457
  • 12
  • 86
  • 114
anishthecoder
  • 940
  • 6
  • 20
  • [`Platform.startUp()`](https://docs.oracle.com/javase/9/docs/api/javafx/application/Platform.html#startup-java.lang.Runnable-) method is new for Java 9. I had not seen it before. It would be quite unusual to call this method, usually you would just extend [Application](https://docs.oracle.com/javase/9/docs/api/javafx/application/Application.html). – jewelsea Jan 26 '18 at 22:05
  • 1
    @jewelsea I'm having to do an explicit `Platform.startUp()` because this problem came up in the context of an application that is a mix of legacy Swing elements and newer FX elements. The application itself starts within the Swing environment (for now). – anishthecoder Jan 26 '18 at 22:23

1 Answers1

2

What the issue is

Your example is a bit ambiguous. You don't set the preferred size of anything added to the Stage at any time. So, the JavaFX platform can really do whatever it wants in terms of sizing things. Setting a preferred percent size is not the same as setting a preferred absolute size. A percent size is relative, so the question becomes, relative to what? and the answer to that is unclear.

As to why this occurs:

// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");

I couldn't say. My guess is that the use of CSS is triggering some additional layout logic which effects the resizing in the absence of any size hints.

How to fix it

Anyway, the solution is just to make things more clear and specify preferred sizing for at least something in the application, then the application will initially be sized to that preferred sizing.

Here is an example:

import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Starter {
    public static void main(String[] args) {
        Platform.startup(() -> {});
        Platform.runLater(() -> {
            final GridPane gridPane = new GridPane();
            final Scene scene = new Scene(gridPane);
            final Stage stage = new Stage();
            stage.setScene(scene);

            final List<StackPane> panes = new ArrayList<>();
            for (int i = 0; i < 4; i++) {

                // Create a new pane with a random background color for
                // illustration
                final StackPane p = createNewPane();
                panes.add(p);

                // The addition / removal of the following line affects the
                // layout.
                p.setStyle("-fx-border-width:2px;-fx-border-color:red");
            }

            for (int r = 0; r < 2; r++) {
                final RowConstraints rc = new RowConstraints();
                rc.setPercentHeight(50);
                gridPane.getRowConstraints().add(rc);
            }
            for (int c = 0; c < 2; c++) {
                final ColumnConstraints cc = new ColumnConstraints();
                cc.setPercentWidth(50);
                gridPane.getColumnConstraints().add(cc);
            }

            for (int r = 0, i = 0; r < 2; r++) {
                for (int c = 0; c < 2; c++) {
                    gridPane.add(panes.get(i++), c, r);
                }
            }
            stage.show();
        });
    }

    private static final Random random = new Random(42);

    private static StackPane createNewPane() {
        StackPane pane = new StackPane();

        pane.setBackground(
                new Background(
                        new BackgroundFill(
                                randomColor(), null, null
                        )
                )
        );

        pane.setPrefSize(150, 100);

        return pane;
    }

    private static Color randomColor() {
        return Color.rgb(
                random.nextInt(256),
                random.nextInt(256),
                random.nextInt(256)
        );
    }
}

The key part of the solution is the call:

pane.setPrefSize(150, 100);

which sets the preferred size for the stack panes which have been placed in your layout.

Alternatively, rather than doing the bottom up preferred sizing by setting a preferred size on each of the StackPanes, you could also accomplish a similar thing from a top-down perspective by setting appropriate constraints on the GridPane instead, for example:

 gridPane.setPrefSize(300, 200);

Note

I'd advise using a JavaFX Application class instead of Platform.startup() call unless there is a really good reason to use the latter (which there is in this case - interfacing with Swing, as you have noted in your comment).

jewelsea
  • 150,031
  • 14
  • 366
  • 406