Reputation: 9
I can't figure out how to get the borderPane space filled without any space left in the stage. The borderPane cannot be changed to a gridPane because that is not the directions I need to follow. I am looking for a way to resize the borderPane components or make the buttons fill the space completely. As a bonus a way for the buttons and borderPane to change size with the window. The buttons should all be the same size.
This is what I want:
This is what I have:
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.scene.control.*;
import javafx.geometry.*;
public class MagicSquares extends Application {
Button tRButton;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Button tLButton = new Button("1");
Button tCButton = new Button("2");
Button tRButton = new Button("3");
HBox topRow = new HBox(tLButton, tCButton, tRButton);
topRow.getStyleClass().add("a");
Button mLButton = new Button("4");
VBox middleLeft = new VBox(mLButton);
middleLeft.getStyleClass().add("b");
Button mCButton = new Button("5");
HBox middleCenter = new HBox(mCButton);
Button mRButton = new Button("6");
VBox middleRight = new VBox(mRButton);
middleRight.getStyleClass().add("c");
Button bLButton = new Button("7");
Button bCButton = new Button("8");
Button bRButton = new Button("9");
HBox bottomRow = new HBox (bLButton, bCButton, bRButton);
bottomRow.getStyleClass().add("d");
BorderPane bPane = new BorderPane();
bPane.setTop(topRow);
bPane.setLeft(middleLeft);
bPane.setCenter(middleCenter);
bPane.setRight(middleRight);
bPane.setBottom(bottomRow);
Scene scene = new Scene(bPane);
primaryStage.setScene(scene);
primaryStage.show();
scene.getStylesheets().add("styles.css");
}
}
I tried to change the boxes around and use only three vboxs. I added color to see the layout more clearly but still no luck. I have tried alot of resizing as well like Prefwidth, max/min--width with double.MAX.. of the buttons, boxes and borderPane.
Upvotes: 0
Views: 127
Reputation: 209330
If you are just looking for a "loophole" to fill the (unreasonable) requirements that you have to use a BorderPane
to lay out a grid, then here is a workaround that is similar in nature to the answer by @VGR but which overrides the BorderPane
's layoutChildren()
method instead of using listeners.
This arguably has some slight advantages, though this is so far in the realms of "don't do this" (because you are working against the API and should be using a GridPane
) that it really doesn't matter. However: here the layout calculation is only performed when the layout is being done, not on every change of either the width or the height of the border pane (typically the latter would require two computations instead of one). And here there is no need for any additional structure, such as HBox
s or VBox
s.
I did not implement the computePref/Min/Max size methods, which you probably should do too with this approach, but again we are so far outside the intended use of the API it doesn't really matter much.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
Button[] buttons = new Button[9];
for (int i = 0; i < buttons.length; i++) {
buttons[i] = new Button(String.valueOf(i+1));
}
BorderPane root = new BorderPane() {
@Override
protected void layoutChildren() {
int numCols = 3;
int numRows = buttons.length / numCols;
if (numRows * numCols < buttons.length) numRows++;
double width = getWidth()/numCols;
double height = getHeight()/numRows;
for (int i = 0 ; i < buttons.length; i++) {
buttons[i].resizeRelocate(
width * (i % numCols),
height * (i / numRows),
width,
height
);
}
}
};
root.getChildren().addAll(buttons);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Upvotes: 1
Reputation: 159291
Place your GridPane
containing your buttons in the center of your BorderPane
.
Add appropriate constraints to allow the buttons to be resizable squares.
Use a listener on the scene size to adjust constraints as needed.
The _
notation requires Java 22, if using a lesser version of Java, then use variable names instead.
The BorderPane here isn't doing much, but it is doing something. It is keeping the GridPane centered with space in the margins when needed when resizing the screen. A StackPane is simpler and would do the same thing, but the BorderPane would also allow you to add other nodes to the borders if that is something that is required in a more complex layout.
Preferred sizing
Manual resizing
Alternate manual resizing
Sample Code
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class MagicSquares extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
GridPane gridPane = new GridPane();
for (int i = 0; i < 9; i++) {
gridPane.add(
makeButton(i),
i % 3,
i / 3
);
}
Scene scene = new Scene(new BorderPane(gridPane));
stage.setScene(scene);
stage.show();
scene.widthProperty().addListener((_, _, _) -> constrain(gridPane));
scene.heightProperty().addListener((_, _, _) -> constrain(gridPane));
}
private void constrain(GridPane gridPane) {
double maxSize = Math.min(
gridPane.getScene().getWidth(),
gridPane.getScene().getHeight()
);
gridPane.setMaxSize(maxSize, maxSize);
}
private Button makeButton(int i) {
Button button = new Button(Integer.toString(i+1));
button.setStyle("-fx-font-size: 20px");
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
GridPane.setHgrow(button, Priority.SOMETIMES);
GridPane.setVgrow(button, Priority.SOMETIMES);
return button;
}
}
I feel guilty for upvoting this.
Upvotes: 5
Reputation: 44318
I think it is obvious to everyone that BorderPane is not an appropriate layout for this task. But since that’s the assignment…
You can explicitly update the button sizes from the size of the BorderPane (which is pretty much duplicating what a different layout would do):
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
public class MagicSquares
extends Application {
@Override
public void start(Stage stage) {
Button[] buttons = new Button[9];
for (int i = 0; i < 9; i++) {
buttons[i] = new Button(String.valueOf(i + 1));
buttons[i].setMaxWidth(Double.MAX_VALUE);
buttons[i].setMaxHeight(Double.MAX_VALUE);
}
HBox top = new HBox(buttons[0], buttons[1], buttons[2]);
HBox bottom = new HBox(buttons[6], buttons[7], buttons[8]);
BorderPane pane = new BorderPane();
pane.setTop(top);
pane.setLeft(buttons[3]);
pane.setCenter(buttons[4]);
pane.setRight(buttons[5]);
pane.setBottom(bottom);
pane.widthProperty().addListener(
o -> updateButtonSizes(buttons, pane));
pane.heightProperty().addListener(
o -> updateButtonSizes(buttons, pane));
stage.setScene(new Scene(pane));
stage.setTitle("Magic Squares");
stage.show();
}
private void updateButtonSizes(Button[] buttons,
BorderPane parent) {
double width = parent.getWidth() / 3;
double height = parent.getHeight() / 3;
for (Button button : buttons) {
button.setPrefWidth(width);
button.setPrefHeight(height);
}
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 3