GridPane doesn't align in center - java

I am making a Login Screen with a number pad, and I can't seem to center align a GridPane of buttons in a Pane. What am I doing wrong?
Main.java
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.input.KeyCombination;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args){
launch(args);
}
public void start(Stage primaryStage) throws Exception{
Rectangle2D bounds = Screen.getPrimary().getBounds();
LoginScreen loginScreen = new LoginScreen(bounds.getWidth(), bounds.getHeight());
Scene scene = new Scene(loginScreen.get());
primaryStage.setScene(scene);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.show();
primaryStage.setFullScreen(true);
}
}
LoginScreen.java
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
public class LoginScreen {
private Pane root;
private GridPane numberPad;
public LoginScreen(double screenWidth, double screenHeight){
root = new Pane();
root.setPrefSize(screenWidth, screenHeight);
root.setBackground(new Background(new BackgroundFill(Color.AQUA, CornerRadii.EMPTY, Insets.EMPTY)));
numberPad = new GridPane();
Button button01 = new Button("1");
Button button02 = new Button("2");
Button button03 = new Button("3");
Button button04 = new Button("4");
Button button05 = new Button("5");
Button button06 = new Button("6");
Button button07 = new Button("7");
Button button08 = new Button("8");
Button button09 = new Button("9");
numberPad.setAlignment(Pos.CENTER);
numberPad.add(button01, 0, 0);
numberPad.add(button02, 1, 0);
numberPad.add(button03, 2, 0);
numberPad.add(button04, 0, 1);
numberPad.add(button05, 1, 1);
numberPad.add(button06, 2, 1);
numberPad.add(button07, 0, 2);
numberPad.add(button08, 1, 2);
numberPad.add(button09, 2, 2);
root.getChildren().addAll(numberPad);
}
public Pane get(){
return root;
}
}
GUI code is verbose, and this post editor isn't letting me post my question as is, so I need these extra lines to get it to accept my question. If I thought I could cut down my code to just the numberPad.setAlignment(Pos.Center) and still make it clear how I am attempting to center my GridPane I most certainly would. I do humbly thank those who might lend me their time to help me solve this issue I have.
Edit 01:
My issue is that the GridPane itself is drawn in the top left corner of the screen rather than in the center of the screen.

You need to actually set the alignment for the parent container. A Pane is not a valid container for doing this, however.
If you were to use a VBox instead, you could simply set its alignment as so:
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
That will cause all of the children of the VBox to be placed in the center.
The Pos enum also provides other methods of positioning, including TOP_CENTER, TOP_LEFT, and BOTTOM_RIGHT, for example.

Related

JavaFX split space evenly between two side panels in an HBox

I have an HBox that contains a square VBox in the center. The HBox width can be made larger than it's height, leaving extra space on the sides. I want to fill this extra space on the left and right with separate VBoxes where I will put buttons, info, etc. I want these two VBoxes, the "side panels," to always be of equal width. However, as you can see from the image below, when the right panel has a button and the left does not, it is wider than the left panel:
I thought
rightPanel.minWidthProperty().bindBidirectional(leftPanel.minWidthProperty());
rightPanel.prefWidthProperty().bindBidirectional(leftPanel.prefWidthProperty());
would do the trick, but it didn't.
Here's my code:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class FX010 extends Application{
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
GamePanel gp = new GamePanel();
Scene scene = new Scene(gp, 800, 600);
primaryStage.setTitle("testing");
primaryStage.minWidthProperty().bind(primaryStage.heightProperty());
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
}
public class GamePanel extends HBox{
public GamePanel() {
this.setMinHeight(400);
final VBox boardBox = new VBox();
boardBox.alignmentProperty().set(Pos.CENTER);
this.alignmentProperty().set(Pos.CENTER);
VBox leftPanel, rightPanel;
leftPanel = new VBox();
rightPanel = new VBox();
leftPanel.setBorder(new Border(new BorderStroke(Color.DARKRED, BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,new BorderWidths(1))));
rightPanel.setBorder(new Border(new BorderStroke(Color.DARKRED, BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,new BorderWidths(1))));
rightPanel.minWidthProperty().bindBidirectional(leftPanel.minWidthProperty());
rightPanel.prefWidthProperty().bindBidirectional(leftPanel.prefWidthProperty());
rightPanel.getChildren().add(new Button("testtesttest"));
HBox.setHgrow(leftPanel, Priority.ALWAYS);
HBox.setHgrow(rightPanel, Priority.ALWAYS);
StackPane board = new StackPane(new Rectangle(100,100,Color.RED));
board.setBorder(new Border(new BorderStroke(Color.DARKBLUE, BorderStrokeStyle.DASHED,
CornerRadii.EMPTY,new BorderWidths(1))));
final NumberBinding binding = Bindings.min(widthProperty(), heightProperty());
boardBox.prefWidthProperty().bind(binding);
boardBox.prefHeightProperty().bind(binding);
boardBox.setMaxSize(Control.USE_PREF_SIZE, Control.USE_PREF_SIZE);
VBox.setVgrow(board, Priority.ALWAYS);
boardBox.getChildren().add(board);
getChildren().addAll(leftPanel, boardBox, rightPanel);
//HBox.setHgrow(this, Priority.ALWAYS);
}
}
}
How can I force leftPanel to always have the same width as rightPanel, while still ensuring that boardBox is always a square?
Thanks!
It works when you add e. g.:
leftPanel.setPrefWidth(100d);
rightPanel.setPrefWidth(100d);
Edit: Maybe it would be better if you use a GridPane as a root layout where you can have three columns with a fixed percentage width like e. g.: 15 % (left-hand side), 70 % (center) and 15 % (right-hand side). But only you can know and decide what fits best for your project. :-P
After a lot of testing, I got this to work:
public class GamePanel extends HBox{
Pane leftPanel, rightPanel, iLeft, iRight;
VBox boardBox;
public GamePanel() {
this.setMinHeight(400);
iLeft = new Pane();
boardBox = new VBox();
iRight = new Pane();
boardBox.alignmentProperty().set(Pos.CENTER);
this.alignmentProperty().set(Pos.CENTER);
leftPanel = new Pane();
rightPanel = new Pane();
leftPanel.setBorder(new Border(new BorderStroke(Color.DARKRED, BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,new BorderWidths(1))));
rightPanel.setBorder(new Border(new BorderStroke(Color.DARKRED, BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,new BorderWidths(1))));
rightPanel.setPrefWidth(0);
leftPanel.setPrefWidth(0);
iRight.getChildren().add(new Button("testtesttest"));
rightPanel.getChildren().add(iRight);
HBox.setHgrow(leftPanel, Priority.ALWAYS);
HBox.setHgrow(rightPanel, Priority.ALWAYS);
StackPane board = new StackPane(new Rectangle(100,100,Color.RED));
board.setBorder(new Border(new BorderStroke(Color.DARKBLUE, BorderStrokeStyle.DASHED,
CornerRadii.EMPTY,new BorderWidths(1))));
final NumberBinding binding = Bindings.min(widthProperty(), heightProperty());
boardBox.prefWidthProperty().bind(binding);
boardBox.prefHeightProperty().bind(binding);
boardBox.setMaxSize(Control.USE_PREF_SIZE, Control.USE_PREF_SIZE);
VBox.setVgrow(board, Priority.ALWAYS);
boardBox.getChildren().add(board);
getChildren().addAll(leftPanel, boardBox, rightPanel);
//HBox.setHgrow(this, Priority.ALWAYS);
}
}
This solution involves setting the left and right panel's prefWidth to zero, then adding internal panes into each panel, which the content is then added to. I think the reason that this works is because space is allocated for the prefWidth before the panel's size is expanded by the Hgrow constraint. Since equal amounts of space are added to each panel by the HBox, the panel with a greater prefWidth ends up bigger. By setting both to zero, they both start with no prefWidth and thus end up at the same size. I believe that's also why anko's answer seemed to work - because when they start with equal prefWidths and the same amount of space is added to each, they end up with the same size. However, setting a prefWidth of 100 messes with the boardBox's size (makes it not a square) when the stage's width is shrunk as small as possible.
I'm sure there is a better way to do this, and I'd be happy to accept a better answer.

How to align a ListView BASELINE in a GridPane

I'm currently writing a Java-FX program that contains a ListView in a GridPane. I want to align the listview by the baseline of its first item with the label to the left of the listview, but the listview is initially empty. It appears not aligned correctly, but jumps to the right position when it is populated and an item is selected.
I asume, in order to calculate the correct baseline offset, the listview needs to know its items before it is shown. So, how can I create an empty listview and fill it dynamically (by user actions), when it should be aligned BASELINE?
You can reproduce the effect with the following mcve (Java 8 u221):
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Stage;
public class ListViewAlignmentTest extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage stage)
{
ListView<String> listview = new ListView<String>();
listview.setPrefSize(100.0, 100.0);
RowConstraints rc0 = new RowConstraints();
rc0.setValignment(VPos.BASELINE);
RowConstraints rc1 = new RowConstraints();
rc1.setValignment(VPos.BASELINE);
GridPane root = new GridPane();
root.getRowConstraints().add(0, rc0);
root.getRowConstraints().add(1, rc1);
root.setHgap(10);
root.setVgap(10);
root.add(new Label("Some text"), 0, 0);
root.add(new TextField(), 1, 0);
root.add(new Label("Other text"), 0, 1);
root.add(listview, 1, 1);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
listview.getItems().addAll("String1", "String2");
}
}
This is how the GUI looks initially: https://i.imgur.com/nUXqyDm.png
After selecting the first listview item it looks correct: https://i.imgur.com/i73kbKw.png
I expect the listview to be aligned BASELINE to the label next to it, but it first appears under the label.
Edit: updated complete code.
When replicated it so that it looks like as shown in image, https://i.imgur.com/i73kbKw.png
Code below makes it immovable.
import javafx.application.Application;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Stage;
public class ListViewAlignmentTest extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage stage)
{
ListView<String> listview = new ListView<String>();
listview.setPrefSize(100.0, 100.0);
RowConstraints rc0 = new RowConstraints();
rc0.setFillHeight(true);
RowConstraints rc1 = new RowConstraints();
rc1.setFillHeight(true);
GridPane root = new GridPane();
root.getRowConstraints().add(0, rc0);
root.getRowConstraints().add(1, rc1);
root.setHgap(10);
root.setVgap(10);
root.add(new Label("Some text"), 0, 0);
root.add(new TextField(), 1, 0);
root.add(new Label("Other text"), 0, 1);
root.add(listview, 1, 1);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
listview.getItems().addAll("String1", "String2");
}
}
As a result, it does not move now. That should help.

Divide stage into 2 gridpanes JavaFX

So Im trying to have text on the left and buttons on the right, text should have constant size and buttons should resize to fill the rest of the window.
Here is my result so far:
I dont want my text over buttons, I want them to share the whole window.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
GridPane buttons = new GridPane();
GridPane textGrid = new GridPane();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Button button1 = new Button();
Button button2 = new Button();
Button button3 = new Button();
Button button4 = new Button();
Button button5 = new Button();
button1.setText("Button1");
button2.setText("Button4");
button3.setText("Button3");
button4.setText("Button4");
button5.setText("Button5");
TextArea text1 = new TextArea();
text1.setText("Test");
text1.setPrefSize(100, 100);
button1.prefWidthProperty().bind(buttons.widthProperty());
button2.prefWidthProperty().bind(buttons.widthProperty());
button3.prefWidthProperty().bind(buttons.widthProperty());
button4.prefWidthProperty().bind(buttons.widthProperty());
button5.prefWidthProperty().bind(buttons.widthProperty());
button1.prefHeightProperty().bind(buttons.heightProperty());
button2.prefHeightProperty().bind(buttons.heightProperty());
button3.prefHeightProperty().bind(buttons.heightProperty());
button4.prefHeightProperty().bind(buttons.heightProperty());
button5.prefHeightProperty().bind(buttons.heightProperty());
buttons.addColumn(0, button1, button2, button3, button4, button5);
textGrid.addColumn(0, text1);
Scene scene = new Scene(root, 280, 180);
root.getChildren().addAll(buttons, textGrid);
buttons.setAlignment(Pos.TOP_RIGHT);
textGrid.setAlignment(Pos.TOP_LEFT);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It is usually better to let the layout panes handle the layout management rather than trying to manage the layout through bindings.
Here is a sample:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class Main extends Application {
private static final int N_BUTTONS = 5;
#Override
public void start(Stage stage) {
VBox buttonLayout = new VBox(
10,
IntStream.range(0, N_BUTTONS)
.mapToObj(this::createButton)
.toArray(Button[]::new)
);
HBox.setHgrow(buttonLayout, Priority.ALWAYS);
TextArea textArea = new TextArea("Test");
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
HBox layout = new HBox(10, textArea, buttonLayout);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private Button createButton(int i) {
Button button = new Button("Button " + i);
// button.setMaxWidth(Double.MAX_VALUE);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
VBox.setVgrow(button, Priority.ALWAYS);
return button;
}
public static void main(String[] args) {
launch(args);
}
}
Here are a couple of things I would point out based upon the sample:
As the buttons are so similar, create the buttons in a loop rather than individually in code. I use an IntStream range with a map and a toArray, but you could do the same thing with a standard for loop (which may be easier to understand).
Use combinations of standard layout panes to achieve your layout. For example the buttons are vertically spaced, so put them in a VBox, the text and the buttons are horizontal to each other, so use a HBox.
Use constraints on the layouts to massage them into performing the layout you like, for example, HBox.setHgrow(buttonLayout, Priority.ALWAYS); tells the Box to always assign any extra additional space in the Box to the buttonLayout so that the buttons will fill any remaining area.
Set constraints on the individual nodes to size them how you wish, for example the following code establishes a fixed width for the textArea, which will not vary (you could similar code to establish a fixed height if you wished):
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
Some controls will automatically expand themselves beyond their max size, buttons do not by default, to enable this behavior use the following code (if you only wanted the width to expand and not the height then you would only set the maxWidth rather than the maxSize):
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
Rather than defining layouts in code as in this example, instead use a tool such as SceneBuilder to create the scene visually and save the layout as an FXML file, so that the layout is separated from your code (similarly place any styling in an external CSS file).

GridPane not visible unless i resize window javaFx

when i start the application, TitledPane does not show the GridPane I have added. Surprisingly it's visible the moment i increase/decrease the window width. where am i missing?
Here is the Complete Code:
package com.ct.bic.comparator;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
public class Comparator extends Application {
#Override
public void start(Stage stage) {
GridPane dbGrid = new GridPane();
dbGrid.setId("dbGrid");
dbGrid.setAlignment(Pos.TOP_LEFT);
dbGrid.setHgap(10);
dbGrid.setVgap(10);
dbGrid.setPadding(new Insets(25, 25, 25, 25));
Label dbConnection = new Label("Database Configuration");
dbConnection.setId("dbConnection");
dbConnection.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20));
dbGrid.add(dbConnection, 0, 0, 2, 1);
Label server = new Label("Server");
server.setId("server");
dbGrid.add(server, 0, 1);
TextField serverText = new TextField();
serverText.setId("serverText");
dbGrid.add(serverText, 1, 1);
Label database = new Label("Database");
database.setId("database");
dbGrid.add(database, 0, 2);
TextField databaseText = new TextField();
databaseText.setId("databaseText");
dbGrid.add(databaseText, 1, 2);
Label user = new Label("User");
user.setId("user");
dbGrid.add(user, 0, 3);
TextField userText = new TextField();
userText.setId("userText");
dbGrid.add(userText, 1, 3);
Label password = new Label("Password");
password.setId("password");
dbGrid.add(password, 0, 4);
PasswordField passwordText = new PasswordField();
passwordText.setId("passwordText");
dbGrid.add(passwordText, 1, 4);
dbGrid.setId("passwordText");
/*GridPane dbGrid = DatabaseInputGrid.getDatabaseGrid();*/
TitledPane tp = new TitledPane("Database Configuration", dbGrid);
Scene scene = new Scene(tp, 500,500);
stage.setScene(scene);
stage.setTitle("Bic-Java Output Comparator Pro");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I don't know the exact reason why, but it seems TitledPane is not resized if used as root for the Scene.
I tried something like this:
VBox vbox = new VBox();
vbox.getChildren().add(tp);
Scene scene = new Scene(vbox, 500,500);
Then everything works as expected.
Edit:
I have found this here:
Do not explicitly set the minimal, maximum, or preferred height of a
titled pane, as this may lead to unexpected behavior when the titled
pane is opened or closed.
So my guess is, when you set your TitledPane as root for the scene, it will try to set the preferred height, that leads to the mentioned "unexpected behaviour".

JavaFX 8: Stage insets (window decoration thickness)?

How can i determine the stage/window insets in JavaFX? In Swing i could simple write:
JFrame frame = new JFrame();
Insets insets = frame.getInsets();
What would be the equivalent in JavaFX to get the size of the border and the titlebar of the window?
You can determine these by looking at the bounds of the scene relative to the width and height of the window.
Given a Scene scene;, scene.getX() and scene.getY() give the x and y coordinates of the Scene within the window. These are equivalent to the left and top insets, respectively.
The right and bottom are slightly trickier, but
scene.getWindow().getWidth()-scene.getWidth()-scene.getX()
gives the right insets, and similarly
scene.getWindow().getHeight()-scene.getHeight()-scene.getY()
gives the bottom insets.
These values will of course only make sense once the scene is placed in a window and the window is visible on the screen.
If you really want an Insets object you can do something like the following (which would even stay valid if the border or title bar changed size after the window was displayed):
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class WindowInsetsDemo extends Application {
#Override
public void start(Stage primaryStage) {
Label topLabel = new Label();
Label leftLabel = new Label();
Label rightLabel = new Label();
Label bottomLabel = new Label();
VBox root = new VBox(10, topLabel, leftLabel, bottomLabel, rightLabel);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 600, 400);
ObjectBinding<Insets> insets = Bindings.createObjectBinding(() ->
new Insets(scene.getY(),
primaryStage.getWidth()-scene.getWidth() - scene.getX(),
primaryStage.getHeight()-scene.getHeight() - scene.getY(),
scene.getX()),
scene.xProperty(),
scene.yProperty(),
scene.widthProperty(),
scene.heightProperty(),
primaryStage.widthProperty(),
primaryStage.heightProperty()
);
topLabel.textProperty().bind(Bindings.createStringBinding(() -> "Top: "+insets.get().getTop(), insets));
leftLabel.textProperty().bind(Bindings.createStringBinding(() -> "Left: "+insets.get().getLeft(), insets));
rightLabel.textProperty().bind(Bindings.createStringBinding(() -> "Right: "+insets.get().getRight(), insets));
bottomLabel.textProperty().bind(Bindings.createStringBinding(() -> "Bottom: "+insets.get().getBottom(), insets));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Categories