I am trying to change hbox cell width via changing child node width in the hbox
Hbox hbox = new Hbox(new Label("node1"), new Label(node2), new Label(node3));
//Lets say I wanna change the lable says "node3"
//i tried below option. did not work
hbox.getChildren().get(2).maxWidth(150);
node.maxWidth() is invoked by layouts to query the maximum size of the node, to change child width (label) cast it then use the setter
((Label) hBox.getChildren().get(2)).setPrefWidth(150);
Related
When I run the following code in the start method of my Main (JavaFX) class I get weird results. The window gets displayed but pane (with a green border) has a width of 0. It is supposed to have the same width as the container's height since I binded prefWidth to the height property.
Then, when I resize the window, the binding comes into effect and the pane becomes a square. Notice that if I maximize the window it also doesn't apply the bindings.
Thank you!
//Create a pane with a min width of 10 and a green border to be able to see it
Pane pane = new Pane();
pane.setStyle("-fx-border-color: green; -fx-border-width: 2");
//Bind the pane's preferred width to the pane's height
pane.prefWidthProperty().bind(pane.heightProperty());
//Put the pane in a vbox that does not fill the stage's width and make the pane grow in the vbox
VBox container = new VBox(pane);
container.setFillWidth(false);
VBox.setVgrow(pane, Priority.SOMETIMES);
//Show the vbox
primaryStage.setScene(new Scene(container, 600, 400));
primaryStage.show();
The problem you are running into here is that when the container is laid out, it has no reasonable information as to the order in which it should compute the width and the height of the pane. So essentially what happens is it computes the width, which (since it's empty), is zero; then computes the height (which fills the container, since you told the VBox to do that). After that, the prefWidth property is changed, but by then the actual width has already been set, so it's essentially too late. The next time a layout pass occurs, the new pref width is taken into account.
I haven't checked the actual layout code, but (since the default content bias is null) most likely the layout code for the vbox is going to do something equivalent to the following pseudocode:
protected void layoutChildren() {
// content bias is null:
double prefWidth = pane.prefWidth(-1);
double prefHeight = pane.prefHeight(-1);
// no fill width:
double paneWidth = Math.max(this.getWidth(), prefWidth);
// vgrow, so ignore preferred height and size to height of the vbox:
double paneHeight = this.getHeight();
pane.resizeRelocate(0, 0, paneWidth, paneHeight);
}
The last call actually causes the height of the pane to change, which then causes the prefWidth to change via the binding. Of course, that's too late for the current layout pass, which has already set the width based on the previous preferred width calculation.
Basically, relying on bindings to manage layout like this is not a reliable way of doing things, because you are changing properties (such as prefWidth in this example) during the layout pass, when it may be already too late to resize the component.
The reliable way to manage layout for a pane like this is to override the appropriate layout methods, which are invoked by the layout pass in order to size the component.
For this example, since the width depends on the height, you should return VERTICAL for the contentBias, and you should override computePrefWidth(double height) to return the height (so the width is set to the height):
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane() {
#Override
public Orientation getContentBias() {
return Orientation.VERTICAL ;
}
#Override
public double computePrefWidth(double height) {
return height ;
}
};
pane.setStyle("-fx-border-color: green; -fx-border-width: 2");
//Bind the pane's preferred width to the pane's height
// pane.prefWidthProperty().bind(pane.heightProperty());
//Put the pane in a vbox that does not fill the stage's width and make the pane grow in the vbox
VBox container = new VBox(pane);
container.setFillWidth(false);
VBox.setVgrow(pane, Priority.SOMETIMES);
//Show the vbox
primaryStage.setScene(new Scene(container, 600, 400));
primaryStage.show();
}
Putting a custom node as content on a TitledPane is fairly easy, but how can I set a node as the title of a TitledPane?
Can't really find anything on the subject, to a point where I start thinking it's not possible to do something other than
titledPane.setText("My title")
But that can't be, can it ?
Use the graphic property of the TitledPane:
HBox hbox = ...
TitledPane titledPane = new TitledPane();
titledPane.setGraphic(hbox);
Hi I'm trying to create a simple layout that looks like this using JavaFX.
I would like the user to be able to drag/resize the middle bar. I've tried to make this using a GridPane. But I can't figure out how to get the middle resized. Perhaps GridPane is not the way to go. Both panes will contain a lot of other program code later on. Thanks!
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
stageRef.setMaxWidth(primaryScreenBounds.getWidth());
stageRef.setMaxHeight(primaryScreenBounds.getHeight());
GridPane gridPane = new GridPane();
gridPane.setGridLinesVisible(true);
gridPane.setPadding(new Insets(10));
Scene scene = new Scene(gridPane);
VBox vBoxMain = new VBox();
vBoxMain.setPrefWidth((primaryScreenBounds.getWidth()/5)*4);
vBoxMain.setPrefHeight(primaryScreenBounds.getHeight());
TextArea textArea = new TextArea();
textArea.setWrapText(true);
textArea.setPrefHeight(primaryScreenBounds.getHeight());
vBoxMain.getChildren().addAll(textArea);
vBoxMain.isResizable();
VBox vBoxSide = new VBox();
vBoxSide.setPrefWidth((primaryScreenBounds.getWidth()/5));
vBoxSide.setPrefHeight(primaryScreenBounds.getHeight());
vBoxSide.isResizable();
gridPane.add(vBoxSide, 1,1,1,1);
gridPane.add(vBoxMain, 2,1,4,1);
stageRef.setScene(scene);
stageRef.show();
You could use a SplitPane:
A control that has two or more sides, each separated by a divider,
which can be dragged by the user to give more space to one of the
sides, resulting in the other side shrinking by an equal amount.
Then you add two others containers to this pane allowing the user to change the position of the divider. You can also set minimum widths for each component within the pane or set the position of each divider within your code.
I have these buttons with different size:
Image
How I can make all buttons with same with size?
It depends on layout where the button is located. For example, if you add all the buttons into GridPane or BorderPane, you have to specify each button width to correspond to certain variable. In the following example I wrap all buttons inside VBox, set VBox preference width and tie up all buttons minimum width to it:
VBox vBox = new VBox();
vBox.setPrefWidth(100);
Button btn1 = new Button("Short");
Button btn2 = new Button("Super Long Button");
btn1.setMinWidth(vBox.getPrefWidth());
btn2.setMinWidth(vBox.getPrefWidth());
vBox.getChildren().addAll(btn1, btn2);
It is also worth to mention that there are two ways to specify the button size. You can do it in the java code or specify it in javafx .fxml file. The above method is an example for java code implementation.
You can also unclamp a button's maximum dimensions so it will grow to fill the available space (unlike most nodes, by default a button node has it's max size clamped to it's preferred size so it doesn't usually grow to fill available space). An Oracle tutorial on Tips for Sizing and Aligning Nodes explains this in more detail.
VBox vBox = new VBox();
Button btn1 = new Button("Short");
Button btn2 = new Button("Super Long Button");
btn1.setMaxWidth(Double.MAX_VALUE);
btn2.setMaxWidth(Double.MAX_VALUE);
vBox.getChildren().addAll(btn1, btn2);
using css you can override the preferred width of all buttons like
.button {
-fx-pref-width: 200px;
}
or create your own style class for certain button groups and add the style to the button like:
css:
.my-special-button {
-fx-pref-height: 28px;
-fx-pref-width: 200px;
}
and then set the style to your button with either
fxml:
styleClass="my-special-button"
or in java
myButton.getStyleClass().add("my-special-button");
Is it possible to manage child elements in a HBox, so that the sum of the widths of all child elements is equal to the width of the HBox?
So that elements fill the HBox and no space is left.
The height of the child elements is by default the height of the HBox, so how about the width? I don't want to calculate the width in my program. It would be better if the layout does this automatically, so that no calculation is needed.
It depends what kind of children does the HBox contain. Some of the children may not be resizable nodes. However, generally speaking, you can use HBox.setHgrow() method and set the same Priority for all children of hbox. The explanation is in its javadoc:
Sets the horizontal grow priority for the child when contained by an
hbox. If set, the hbox will use the priority to allocate additional
space if the hbox is resized larger than it's preferred width. If
multiple hbox children have the same horizontal grow priority, then
the extra space will be split evening between them. If no horizontal
grow priority is set on a child, the hbox will never allocate it
additional horizontal space if available. Setting the value to null
will remove the constraint.
Additionally, if you are trying to obtain a grid-like layout then try out other layout options, for instance TilePane or FlowPane and maybe GridPane.
The simple code for your answer would be this:
Button button1 = new Button("Save");
Button button2 = new Button("Delete");
HBox.setHgrow(button1, Priority.ALWAYS);
HBox.setHgrow(button2, Priority.ALWAYS);
It's recommended to use HBox class rather than name of HBox pane.
In FXML:
<HBox>
<Label HBox.hgrow="ALWAYS" maxWidth="Infinity">
TEST
</Label>
</HBox>
Adding to #bdshahab's answer, since HBox will try to resize their children to their preferred widths, they will only fill the box if their combined preferred widths are wider than the HBox width.
So it may be necessary to do something like:
Button button1 = new Button("Save");
Button button2 = new Button("Delete");
HBox.setHgrow(button1, Priority.ALWAYS);
HBox.setHgrow(button2, Priority.ALWAYS);
button1.setPrefWidth(100000);
button2.setPrefWidth(100000);