JavaFx layout: disappearing/unorganized panes - java

I have a background pane that is meant for drawing objects that is not displaying when used in conjunction with a BorderPane, VBox, and a HBox. Furthermore, my BorderPane is not positioning the layout properly (left, bottom, right).
I have tried using multiple different panes and adding the VBox and HBox to them, however I encounter similar errors where the layout is not as expected and the main drawing pane (canvas) disappears.
BorderPane bp = new BorderPane();
button1 = new Button("Undo");
button2 = new Button("Erase");
HBox hb = new HBox(10);
hb.getChildren().addAll(button1, button2);
TilePane tp = new TilePane();
vb = new VBox();
vb.setPadding(new Insets(10, 10, 10, 10));
vb.setSpacing(25);
vb.setStyle("-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: black;");
colorCombo = new ComboBox<String>();
colorCombo.getItems().addAll("Black","Blue","Green","Red","Yellow","Orange","Pink");
colorCombo.setValue("Black");
colorCombo.setMinWidth(100);
ToggleGroup group1 = new ToggleGroup();
rbutton1 = new RadioButton("Rectangle");
rbutton1.setToggleGroup(group1);
rbutton1.setMinWidth(80);
rbutton1.setSelected(true);
rbutton2 = new RadioButton("Circle");
rbutton2.setToggleGroup(group1);
rbutton2.setMinWidth(80);
canvas = new Pane();
canvas.setStyle("-fx-background-color: beige;");
vb.getChildren().addAll(colorCombo, rbutton1, rbutton2);
tp.getChildren().add(vb);
bp.setLeft(tp);
bp.setBottom(hb);
bp.setRight(canvas);
this.getChildren().addAll(bp);
The TilePane and BorderPane was my latest experiment. I used bp.setRight(canvas); to see if I could even get the canvas to show at all even though I want the canvas to be the entire background and not just to the right. The canvas does not show.

I created three separate GridPanes and added the vbox, hbox, and canvas (after setting the minimum height and width) to the gridpanes and then added the gridpanes to a BorderPane. This fixed up the layout for my program.

From BorderPane:
The top and bottom children will be resized to their preferred heights
and extend the width of the border pane. The left and right children
will be resized to their preferred widths and extend the length
between the top and bottom nodes. And the center node will be resized
to fill the available space in the middle.
For any kind of pane that has no children, its preferred width is 0. You need to explicitly set a width.
One side note: Pane would still have preferred width of 0 even if it has children, because Pane does not layout its children to determine its own size.

Related

Buttons in Hbox uneven width with hgrow when distributed width can't be a whole number

I want to have an HBox container with 3 buttons that are even in width, but when the parent's width can't be divided into whole number parts one of the nodes is less. If my HBox is 245px and I have 3 buttons 1 of them is 81px and the others are 82px.
The problem is that on top of the HBox I have a loading circle indicator and the circle is in the center of the HBox and when the middle button is not centered the loading circle also looks uncentered on top of the HBox.
HBox root = new HBox();
root.setFillHeight(true);
for (int i = 0; i < 3; i++) {
AnchorPane pane = new AnchorPane();
HBox.setHgrow(pane, Priority.ALWAYS);
pane.setMaxHeight(Double.MAX_VALUE);
root.getChildren().add(pane);
}
Scene scene = new Scene(root, 245, 50, Color.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.show();
root.getChildren().forEach(node -> {
AnchorPane pane = ((AnchorPane)node);
System.out.println(pane.getWidth());
});
The idea is that its a login scene and after submiting the username and password, the server is loading and there is a circle ProgressIndicator on top of the hbox and the circle is centered on top of it.
The circle looks uncentered on top of the middle button. So how can I do this with a layout container without explicity setting the width of the buttons. Do layouts always devide children on whole numbers?
So after some property testing I found out that snapToPixel=false is the way to go.
From oracle docs about snapToPixel https://docs.oracle.com/javase/8/javafx/api/javafx/scene/layout/Region.html
Defines whether this region adjusts position, spacing, and size values of its children to pixel boundaries. This defaults to true, which is generally the expected behavior in order to have crisp user interfaces. A value of false will allow for fractional alignment, which may lead to "fuzzy" looking borders.
I tried different px and in my case it works fine with the edges.
Also without touching the snapToPixel I found that GridPane with 33.3% columns width palces the 81px button in the middle, where with hbox it's the last child, so GridPane also worked for me.

Setting the alignment of a node without using BorderPane?

So I have a Text node which I want to position at the Top-Right side of the scene. Most methods state to use BorderPane, but in my case some of the other nodes in my scene are using Panewhich is also my root pane, so the obvious workaround is to add the nodes you want to the BorderPane and add the BorderPane to the root Pane.
However BorderPane does not seem to be compatible with the Pane. That is, all the nodes of the BorderPane won't be shown. Also positioning the nodes based on their translate X and Y are not supported in BorderPane as well.(i.e. setLayoutX() does not work.) Because of this I cannot even make my root Pane to BorderPane.
Here is what I've tried:
public class foo {
private Pane root = new Pane();
private BorderPane borderPane = new BorderPane();
private Scene scene = new Scene(root, 1366, 768);
public foo(){
Text text1 = new Text("test1");
text1.setLayoutX(11); // Ignore this if you want.
test1.setLayoutY(11);
Text text2 = new Text("test2");
root.getChildren().add(test1);
borderPane.setRight(text2);
root.getChildren().add(borderPane);
// Display the scene on a stage.
}
}
Here only test1 is shown and test2 is not seen.
Is there a way I can position this text on the RIGHT TOP side of the screen without using BorderPane?
Pane just resizes the children to the preferred size. Since you only place the Text node as child of the BorderPane, its preferred size is the size of the child.
In this case I recommend using a different layout, e.g.
StackPane
This allows you set the alignment of a node within the layout using StackPane.setAlignment(child, newAlignment);. The drawback is that setting an absolute position requires you to either make the child unmanaged (with some side effects) or specifying 4 values (an Insets instance) as margin.
Text text1 = new Text("test1");
StackPane.setMargin(text1, new Insets(11, 0, 0, 11));
StackPane.setAlignment(text1, Pos.TOP_LEFT);
Text text2 = new Text("test2");
StackPane.setAlignment(text2, Pos.TOP_RIGHT);
root = new StackPane(text1, text2);
AnchorPane
This does not allow for center alignments, but it allows you to set absolute positions using layoutX and layoutY just the way you'd do in a Pane. You can set anchor properties to specify the distance of a child from the top, left, right and/or bottom:
Text text1 = new Text("test1");
text1.setLayoutX(11);
text1.setLayoutY(11);
Text text2 = new Text("test2");
AnchorPane.setRightAnchor(text2, 0d);
AnchorPane.setTopAnchor(text2, 0d);
root = new AnchorPane(text1, text2);

How to make a TextField fill all the remaining width of a pane containing multiple nodes?

I need to make a chat-simulating JavaFX application for my assignment (no web functionalities, just two text fields for input and two text areas for output). I need to have a “Send” button next to my text field. I cannot make the text field to fill up the width of my window without “squishing” the button at startup, similarly to how swing’s boxLayout does that?
I bound the field’s width to the parent pane’s width, subtracting the button’s width and the pane’sspacing and it works after I start resizing the window, but when the application first starts up, the button’s text is not fully visible.
public void start(Stage stage_main) throws Exception {
//Pane creation and nesting:
HBox pane_main = new HBox();
Scene scene_main = new Scene(pane_main, 480, 360);
BorderPane pane_left_parent = new BorderPane();
BorderPane pane_right_parent = new BorderPane();
HBox pane_left_bottom = new HBox();
HBox pane_right_bottom = new HBox();
pane_main.getChildren().addAll(pane_left_parent, pane_right_parent); //Focusing only on the left pane for now for testing.
pane_left_parent.setBottom(pane_left_bottom);
//Contents creation and nesting:
TextArea textA_left = new TextArea("Testing...");
Button button_left = new Button("Send");
TextField textF_left = new TextField("Test input...");
textF_left.prefWidthProperty().bind(pane_left_bottom.widthProperty().subtract(button_left.widthProperty()).subtract(pane_left_bottom.spacingProperty()));
//Placing contents in panes:
pane_left_parent.setCenter(textA_left);
pane_left_bottom.setSpacing(3);
pane_left_bottom.getChildren().addAll(textF_left, button_left);
//Finishing up:
stage_main.setScene(scene_main);
stage_main.show();
}
Is there any way to have the button have it’s “best” size already at startup without manually setting any widths in pixels, just like in swing?
Don't bind the prefWidth to the parent. If you want a child of an HBox to grow horizontally you can set an hgrow constraint on it:
HBox.setHgrow(theChild, Priority.ALWAYS);
Then let the HBox handle sizing and positioning the child node. And as you noted, in order to stop the Button from shrinking as the HBox changes size you need to set its minWidth. However, you should use:
button.setMinWidth(Region.USE_PREF_SIZE);
If you use getWidth() you might accidentally call it before the Button actually has a non-zero width. Plus, using USE_PREF_SIZE means the minWidth will stay up-to-date with the prefWidth (if you change it for whatever reason).
Some links:
HBox#.setHgrow(Node,Priority)
Priority
Region#USE_PREF_SIZE

JavaFX 8: How to be able to re-size window (stage) but keep inner elements at their positions?

I have a project that has a 2 text areas and few buttons. The root pane is a AnchorPane. when resizing the window to smaller window, all the elements start overlap. What methods can fix this? (IGNORE THE NAME OF MY anchorpane, i got lazy)
AnchorPane borderpane = new AnchorPane ();
TextArea user_list = new TextArea();
user_list.setPrefSize(150, 400);
TextArea messages = new TextArea();
messages.setPrefSize(350, 400);
TextField typebox = new TextField();
typebox.setPrefSize(425, 100);
// put a shape over a text, over a shape
StackPane send_container = new StackPane();
Rectangle send_box = new Rectangle(75, 25);
Label send_text = new Label("Send");
send_container.getChildren().add(send_box);
send_container.getChildren().add(send_text);
AnchorPane.setLeftAnchor(messages, 25.0);
AnchorPane.setTopAnchor(messages, 10.0);
AnchorPane.setRightAnchor(user_list, 25.0);
AnchorPane.setTopAnchor(user_list, 10.0);
AnchorPane.setBottomAnchor(typebox, 25.0);
AnchorPane.setLeftAnchor(typebox, 25.0);
AnchorPane.setBottomAnchor(send_container, 25.0);
AnchorPane.setRightAnchor(send_container, 25.0);
borderpane.getChildren().addAll(messages, user_list, typebox,send_container );
Scene scene = new Scene(borderpane, 600, 600);
primaryStage.setMaxHeight(600);
primaryStage.setMaxWidth(600);
primaryStage.setScene(scene);
primaryStage.setTitle("Welcome");
scene.getStylesheets().add(LoginWindow.class.getResource("Login.css").toExternalForm());
primaryStage.show();
You are hard-coding the locations and sizes of your controls. This means the controls cannot respond to changes in the size of their parent nodes.
Usually, you should not specify any heights or widths. Controls all have default preferred sizes, and all layouts respect those. Layouts also decide how child nodes will be resized in response to the user's resizing of a window.
Often, the layout of a window needs to be broken down into sub-layouts. In your case, you want one section that always resizes to fill the window (the user list and message section), with another section at the bottom (the typebox and Send button). A BorderPane is the ideal choice, since its center node always fills it. So the center of this main BorderPane would contain the user list and message area, while the bottom of this BorderPane would contain the typebox and the Send button.
You probably want the user to be able to horizontally resize both the user list and the messages, so I'd put them in a SplitPane, and make that SpiltPane the center of the main BorderPane.
You probably want the typebox and Send button to be in a separate child BorderPane, with the typebox as the center node, since you want the typebox to stretch and shrink, horizontally, when the user resizes the window.
So, to summarize:
user list and message area in a SplitPane
typebox and Send button in a BorderPane
parent BorderPane with user list/message section in the center, typebox/Send section on the bottom
The code for this is actually pretty short:
ListView user_list = new ListView();
TextArea messages = new TextArea();
messages.setPrefRowCount(12);
messages.setPrefColumnCount(30);
TextField typebox = new TextField();
typebox.setPrefColumnCount(30);
Button send_text = new Button("Send");
send_text.disableProperty().bind(
typebox.lengthProperty().lessThan(1));
SplitPane top = new SplitPane(user_list, messages);
top.setDividerPosition(0, 1/3.0);
BorderPane bottom = new BorderPane();
bottom.setCenter(typebox);
bottom.setRight(send_text);
BorderPane.setMargin(typebox, new Insets(0, 12, 0, 0));
BorderPane main = new BorderPane();
main.setCenter(top);
main.setBottom(bottom);
BorderPane.setMargin(bottom, new Insets(12));
Scene scene = new Scene(main);
primaryStage.setScene(scene);
primaryStage.setTitle("Welcome");
scene.getStylesheets().add(LoginWindow.class.getResource("Login.css").toExternalForm());
primaryStage.show();
Notice that there are no hard-coded dimensions or coordinates (except the margins defined by the Insets objects). Every control has a preferred size based on its properties, such as a TextField's preferred column count.
The workings of the various layouts are well documented. I suggest reading about them in the javafx.scene.layout package.
(I'm guessing the user list should be a ListView, not a TextArea, since typical chat programs allow selection of one or more users. And I suspect your black Rectangle and send_text Label were intended to represent a disabled Button.)
Use a pane other than a Anchor Pane. It's for absolute positioning. Try a stack pane or simple VBox.

ImageView in JavaFX not keeping proper size

I am using this format to place an image as a background picture.
final VBox mainroot = new VBox();
mainroot.getChildren().addAll(backbutton,heroship);
final StackPane mainstackPane = new StackPane();
mainstackPane.getChildren().addAll(iv2,mainroot);
final HBox hbox = new HBox(mainstackPane);
iv2 is my image iteself, created like this
final Image imagegame = new Image(bcolor,width,height, false, false);
ImageView iv2 = new ImageView();
iv2.setImage(imagegame);
iv2.setPreserveRatio(true);
iv2.setFitWidth(width);
iv2.setFitHeight(height);
As well, I am adding a set amount of Labels using an array and a for loop,
Label[] alienship = new Label[10];
for (int i=0;i<alienship.length;i++)
{
mainroot.getChildren().add(alienship[i]);
}
My problem is, every time I change the array of Labels size lets say from 10 to 20, the image itself shifts downwards and I would have to translate it back up to fit.
I have tried using both a VBox and an HBox but I am not able to see a difference in my result.
I have also tried to use .setFitWidth() and .setFitHeight() but nothing helped.
With Label[] alienship = new Label[10];
Result: http://puu.sh/eAihN/c5c7f38ef0.jpg
(In this case I would have used .setTranslate to allign it back up.
With Label[] alienship = new Label[20];
Result: http://puu.sh/eAiqz/3b4280b63b.jpg
What can I do to fix this issue and ensure it stays alligned no matter what size I set the array to?
Thanks.
Problem
The problem is not with the ImageView, but with the Layout/Container that you chose to add heroship and alienship.
VBox is a layout whose height depends on the no of children and height of each child. When the number of alienship increases the VBox height will increase to accommodate all the children.
Solution
You must consider a layout which can add any number of children, without any effect on the width and height of the container. Consider StackPane
Quick fix is to make the VBox as the parent of the StackPane. Add the Image and the space ships to the StackPane, then add the Button and the StackPane to your VBox. Finally adding the VBox to your HBox.
final StackPane mainstackPane = new StackPane();
mainstackPane.getChildren().addAll(iv2, heroship);
final VBox mainroot = new VBox();
mainroot.getChildren().addAll(backbutton, mainstackPane);
final HBox hbox = new HBox(mainroot);
Add all your alienships to the StackPane instead of adding them to the VBox
Label[] alienship = new Label[10];
for (int i=0;i<alienship.length;i++)
{
mainstackPane.getChildren().add(alienship[i]);
}

Categories

Resources