BorderPane not showing other pane except for center Javafx

Viewed 280

I have this piece of code here that has a borderpane as a parent and 2 other panes, one is a minesweeper game and the other is an empty pane with a black background, the minesweeper game is set to be in the center of the borderpane and the empty pane is set to be in the bottom. Right now when I run the code, it will only show the minesweeper game and not the empty pane (can be seen in the image). How do I make it show the empty pane at the bottom of the borderpane?

(Layout as of right now)

enter image description here

package project;

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

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class MinesweeperApp extends Application {

    private static final int TILE_SIZE = 40;
    private static final int W = 800;
    private static final int H = 800;

    private static final int X_TILES = W / TILE_SIZE;
    private static final int Y_TILES = H / TILE_SIZE;

    private Tile[][] grid = new Tile[X_TILES][Y_TILES];
//    private ArrayList[][] grid = new ArrayList[X_TILES][Y_TILES];
    private Scene scene;

    private Parent createContent() {
        BorderPane root = new BorderPane();
        Pane middlepane = new Pane();
        Pane lowerPane = new Pane();
        

        lowerPane.setStyle("-fx-background-color: black;");
        root.setTop(lowerPane);
        root.setBottom(middlepane);
        middlepane.setPrefSize(W, H);

        for (int y = 0; y < Y_TILES; y++) {
            for (int x = 0; x < X_TILES; x++) {
                Tile tile = new Tile(x, y, Math.random() < 0.2);

                grid[x][y] = tile;
                middlepane.getChildren().add(tile);
            }
        }

        for (int y = 0; y < Y_TILES; y++) {
            for (int x = 0; x < X_TILES; x++) {
                Tile tile = grid[x][y];

                if (tile.hasBomb)
                    continue;

                long bombs = getNeighbors(tile).stream().filter(t -> t.hasBomb).count();

                if (bombs > 0)
                    tile.text.setText(String.valueOf(bombs));
            }
        }

        return root;
    }

    private List<Tile> getNeighbors(Tile tile) {
        List<Tile> neighbors = new ArrayList<>();

        // ttt
        // tXt
        // ttt

        int[] points = new int[] {
                -1, -1,
                -1, 0,
                -1, 1,
                0, -1,
                0, 1,
                1, -1,
                1, 0,
                1, 1
        };

        for (int i = 0; i < points.length; i++) {
            int dx = points[i];
            int dy = points[++i];

            int newX = tile.x + dx;
            int newY = tile.y + dy;

            if (newX >= 0 && newX < X_TILES
                    && newY >= 0 && newY < Y_TILES) {
                neighbors.add(grid[newX][newY]);
            }
        }

        return neighbors;
    }

    private class Tile extends StackPane {
        private int x, y;
        private boolean hasBomb;
        private boolean isOpen = false;

        private Rectangle border = new Rectangle(TILE_SIZE - 2, TILE_SIZE - 2);
        private Text text = new Text();

        public Tile(int x, int y, boolean hasBomb) {
            this.x = x;
            this.y = y;
            this.hasBomb = hasBomb;

            border.setStroke(null);
            border.setFill(Color.NAVY);

            text.setFont(Font.font(18));
            text.setText(hasBomb ? "X" : "");
            text.setVisible(false);

            getChildren().addAll(border, text);

            setTranslateX(x * TILE_SIZE);
            setTranslateY(y * TILE_SIZE);

            setOnMouseClicked(e -> open());
        }

        public void open() {
            System.out.println("clicked");
            System.out.println("x: " + this.x + " " + "y: " + this.y);
            if (isOpen){
                return;
            }



            if (hasBomb) {
                System.out.println("Game Over");
                scene.setRoot(createContent());
                return;
            }

            isOpen = true;
            text.setVisible(true);
            border.setFill(Color.RED);

            if (text.getText().isEmpty()) {
                getNeighbors(this).forEach(Tile::open);
            }
        }
    }

    @Override
    public void start(Stage stage) throws Exception {
        scene = new Scene(createContent());
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {

        launch(args);
    }
}
2 Answers

Absent children, an empty Pane has zero width and height. Try adding non-empty content to the top and bottom to see the effect:

root.setTop(new Label("top"));
root.setCenter(middlepane);
root.setBottom(new Label("bottom"));

top


bottom

As an aside, your original root.setBottom(middlepane) may be misleading.

Setting Constraints

Set size constraints on a pane to “see” a Pane which has no content.

This will make the pane take up space in the layout.

To make the app behave as you desire, configure the pane's:

  • min width or height, OR
  • pref width or height, OR
  • any combination of those.

For example, to set a min height for the top pane in a border pane:

BorderPane borderPane = new BorderPane();

Pane top = new Pane();
top.setMinHeight(15);
borderPane.setTop(top); 

Hiding Content

Or you could put some content in the pane and make it invisible until it is needed, while still reserving the space required to display it.

For example:

Label header = new Label("header");
header.setVisible(false);
Pane top = new Pane(header);
borderPane.setTop(top);

This works because things in the scene graph which are not visible still take up layout space, even though they are not displayed, unless you also call setManaged(false).

For the specific case of a label, you wouldn’t need the visibility setting, you could just set the label to an empty string and it would still take up room, even though nothing would be shown.

For myself, I try to avoid sizing hints where I can, so I would use an empty label. Or, set the visibility, for a more complex pane. That way I let the layout engine calculate the appropriate amount of space to reserve.

Spacing Content

This doesn't apply in your case with border pane, but for other layouts like HBox and VBox you might want to push a node all the way right or to the bottom of the layout. You can do that by adding an empty pane before the last node and setting a layout constraint on the pane like this:

HBox.setHgrow(spacer, Priority.ALWAYS);

This technique is explained more in the answer to:

Related