JavaFX listener to a visible part of VBox - javafx

I have a JavaFX VBox inside a BorderPane (central). The content of the VBox is calculated using some business logic and it depends on the height of the visible part of the vbox.
So basically I need a listener watching changes of the visible height of the vbox = height of the central part of the border pane.
The following code demonstrates what I have tried:
public class HelloFX extends Application {
#Override
public void start(Stage primaryStage) {
VBox vbox = new VBox();
vbox.boundsInParentProperty()
.addListener((obs, oldValue, newValue) ->
System.out.println(newValue.getHeight()));
Button button = new Button("ADD LINE");
button.setPrefHeight(25);
button.setOnAction(event ->
vbox.getChildren().add(new Label("line")));
BorderPane borderPane = new BorderPane();
borderPane.setCenter(vbox);
borderPane.setTop(button);
Scene scene = new Scene(borderPane, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
BorderPane with simple button on the top position and VBox on the central. The button click adds one line to vBox. Total scene height is 100, 25 is the button height and the rest (75) is the vBox.
I'm looking for some listener to report changes of the height of the central part of border pane. So in my example it should always print "75" no matter how many lines I have added to the vBox. The only event changing the value should be resizing the whole window. In reality once the vBox is filled my listener reports increasing height values. Apparently the height property includes the invisible part of the vbox.
EDIT
Finally I've found some solution - placing the vBox in the ScrollPane with disabled scrollbars. Then I can simply listen on the height property of the scrollpane and everything works as expected.
public class HelloFX extends Application {
#Override
public void start(Stage primaryStage) {
VBox vbox = new VBox();
ScrollPane scrollPane = new ScrollPane();
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setContent(vbox);
scrollPane.heightProperty()
.addListener((obs, oldValue, newValue) ->
System.out.println(newValue));
Button button = new Button("ADD LINE");
button.setPrefHeight(25);
button.setOnAction(event ->
vbox.getChildren().add(new Label("line")));
BorderPane borderPane = new BorderPane();
borderPane.setCenter(scrollPane);
borderPane.setTop(button);
Scene scene = new Scene(borderPane, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}

Related

JavaFX How to make HBox use only the width it requires inside VBox

I have a HBox inside a VBox and while most questions seem to be asking how to get the HBox to use the whole width of the VBox it is contained in, I require the opposite. I have buttons inside the HBox which constantly vary in amount, thus the HBox should continually alter it's size, but after adding a background colour to the HBox it's clear it occupies the entire width of the VBox, making centring it impossible.
It's currently like the top example, but I need it to be like the bottom example:
And using
HBox.setHgrow(wordButtonsBox, Priority.NEVER);
doesn't change anything either..
public class CentreStuff extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private Region createContent() {
HBox buttonBox1 = new HBox(new Button("Button1"), new Button("Button2"), new Button("Button3"), new Button("Button4"));
buttonBox1.setStyle("-fx-border-color: red;");
VBox results = new VBox(10, buttonBox1);
return results;
}
There are two ways, with slightly different effects depending on what else is in the VBox:
results.setFillWidth(false);
will attempt to resize all the VBox's content to its preferred width, regardless of the width of the VBox. Setting it to true (the default) will size the VBox's content to the width of the VBox, if possible.
buttonBox1.setMaxWidth(Region.USE_PREF_WIDTH);
will prevent the HBox from being wider than its preferred width, so this will keep the HBox at its preferred width. This solution will allow other components in the VBox to be sized to the width of the VBox, if that's what you need.
public class CentreStuff extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private Region createContent() {
HBox buttonBox1 = new HBox(new Button("Button1"), new Button("Button2"), new Button("Button3"), new Button("Button4"));
buttonBox1.setStyle("-fx-border-color: red;");
VBox results = new VBox(10, buttonBox1);
results.setFillWidth(false);
return results;
}
}
or
public class CentreStuff extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private Region createContent() {
HBox buttonBox1 = new HBox(new Button("Button1"), new Button("Button2"), new Button("Button3"), new Button("Button4"));
buttonBox1.setStyle("-fx-border-color: red;");
buttonBox1.setMaxWidth(Region.USE_PREF_SIZE);
VBox results = new VBox(10, buttonBox1);
return results;
}
}
Had to post this as an answer to include code and images.
Don't bother restricting the size of the HBox, just centre its contents:
public class CentreStuff extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private Region createContent() {
Label label = new Label("I am a label");
Text text = new Text("I am a text");
HBox buttonBox1 = new HBox(new Button("Button1"), new Button("Button2"), new Button("Button3"), new Button("Button4"));
buttonBox1.setStyle("-fx-border-color: red;");
buttonBox1.setAlignment(Pos.CENTER);
HBox buttonBox2 = new HBox(new Button("Button1"), new Button("Button2"), new Button("Button3"), new Button("Button4"));
buttonBox2.setStyle("-fx-border-color: red;");
VBox results = new VBox(10, label, text, buttonBox1, buttonBox2);
results.setAlignment(Pos.CENTER);
return results;
}
}
And it looks like this:
Everything makes sense, and the layout looks exactly as it should.
[Edit: To Text as well as Label to show how alignment still works]

How to make a GridPane right of BorderPane closeable?

I have code in the form:
<BorderPane>
...
<right>
<GridPane>
...
</GridPane>
</right>
...
</BorderPane>
Obviously, now the GridPane takes a big space right of my BorderPane. What I'd like to do is add a button (or another element) that minimizes and maximizes the GridPane, so it's only fully in the view of the user when it is really needed. How can I easily achieve this?
You can do what you want by setting the Visible and Managed properties of your GridPane off and on. The centre of the BorderPane will automatically expand to take over the entire width of the BorderPane. "Managed" controls whether or not the layout manager will leave space for the node, so if you just turn Visible off, then you'll have an unused area the size of your GridPane on the right. The following code demonstrates it, I put the buttons in a VBox with a border around it so that you can see how it expands:
public class ResizeRight extends BorderPane {
public ResizeRight() {
Button openButton = new Button("Open");
Button closeButton = new Button("Close");
GridPane gridPane = new GridPane();
gridPane.addRow(0, new Text("This is just some text"));
gridPane.addRow(1, new Text("This is just some more text"));
VBox vbox = new VBox(10, openButton, closeButton);
vbox.setBorder(new Border(new BorderStroke(Color.BLACK,
BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
setCenter(vbox);
setRight(gridPane);
setPadding(new Insets(10));
openButton.setOnAction(evt -> {
gridPane.setVisible(true);
gridPane.setManaged(true);
});
closeButton.setOnAction(evt -> {
gridPane.setVisible(false);
gridPane.setManaged(false);
});
}
}
Run it from something like this:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new ResizeRight(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}

How to make pane and elements in it resizable when stage size is changing

Can you please tell me how can I realize that the whole content of a pane will be resized while the stage is resized with mousedragg. Here is my code:
public class fab extends Application {
private Stage stage;
private Pane pane;
private Scene scene;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
Button button = new Button("Button");
pane = new Pane();
pane.getChildren().add(button);
stage.setTitle("Test");
scene = new Scene(pane, 640, 640);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
I think there is the idea of binding. But I don't know how to use that, in order to make all nodes of a pane resizable, when the stage size is changing.
I'm searching a solution without Fxml or sceneBuilder.
Thank you in advance.
If you insists to use the Pane container then after the line scene = new Scene(pane, 640, 640); add this:
scene.widthProperty().addListener((c,o,n)->button.setPrefWidth((Double)n));
scene.heightProperty().addListener((c,o,n)->button.setPrefHeight((Double)n));
and after the line stage.setScene(scene); add this:
button.setPrefSize(scene.getWidth(), scene.getHeight());
This works fine with Pane and do your required thing.
But I prefer using an AnchorPane container and set the Top, Right,Bottom and Left anchors to 0 .
Here is the solution if you wish to bind the width of the button to you scene width
button.minWidthProperty().bind(scene.widthProperty());
You can also modify this +/- whatever you want for ex
button.minWidthProperty().bind(scene.widthProperty().subtract(20));
and you can do the same for the height
button.minHeightProperty().bind(scene.heightProperty().subtract(200));

JavaFX translateZ for a non-root node causes it to disappear

I'm trying to use the translateZ property on a VBox to move the panel "into the screen".
If I use setTranslateZ() on the root node this works fine. However if I change root.setTranslateZ(200); to panel.setTranslateZ(200); the window is blank.
public class Demo01HelloWorld3D extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Button button = new Button("Press me");
VBox panel = new VBox(button);
panel.setAlignment(Pos.CENTER);
panel.setDepthTest(DepthTest.ENABLE);
VBox root = new VBox(panel);
root.setAlignment(Pos.CENTER);
root.setDepthTest(DepthTest.ENABLE);
root.setTranslateZ(200);
// panel.setTranslateZ(200); <== I want this to work
Scene scene = new Scene(root, 320, 240, true);
primaryStage.setScene(scene);
scene.setCamera(new PerspectiveCamera(false));
primaryStage.show();
}
}
Setting translateZ on the root node
Setting translateZ on the panel node
Things I've tried
Setting depthTest attribute to enable - although I don't think this is necessary as it defaults to DepthTest.INHERIT
Lots of searching for similar questions!
Checking SCENE3D is enabled - yes it is
Checking the javadoc for translateZProperty
checked Z value is less than camera clippingFar property
Looked at Oracle JavaFX 3D tutorial - this does not specifically address 3D with standard controls and containers etc.
With panel.setTranslateZ(200); you're pushing the panel behind the root, so the root obscures it.
Add root.setStyle("-fx-background-color: transparent;"); and it works:
#Override
public void start(Stage primaryStage) throws Exception {
Button button = new Button("Press me");
VBox panel = new VBox(button);
panel.setAlignment(Pos.CENTER);
panel.setDepthTest(DepthTest.ENABLE);
VBox root = new VBox(panel);
root.setAlignment(Pos.CENTER);
root.setDepthTest(DepthTest.ENABLE);
root.setStyle("-fx-background-color: transparent;");
panel.setTranslateZ(200);
Scene scene = new Scene(root, 320, 240, true);
primaryStage.setScene(scene);
scene.setCamera(new PerspectiveCamera(false));
primaryStage.show();
}

How to make javafx.scene.Scene resize while maintaining an aspect ratio?

I'm writing an application with JavaFX 2.2.7-b01.
Here is an example of the code I currently have. How can I allow the application window to be resized but maintain the aspect ratio it is initially configured with? In other words, if the user resizes the window, the window width should always stay double the window height.
...
public void showScene(Stage stage, String fxmlPath) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(fxmlPath);
Parent page;
try (InputStream in = Main.class.getResourceAsStream(fxmlPath)) {
page = (Parent) loader.load(in);
}
Scene scene = new Scene(page, 400, 200);
stage.setScene(scene);
stage.sizeToScene();
stage.show();
} catch (Exception ex) {
...
}
}
...
It seems JavaFX allows a user to specify the width and height of a scene in the constructor but does not allow programmatic access to update the width or height. There are no setWidth or setHeight methods. I know I can add property listeners to get the read only width/height of the scene while it is being resized, but I haven't been able to figure out how to change the scene dimensions dynamically so I can force the aspect ratio to be maintained.
I imagine this would be possible if I were to subclass the Scene object (if I have to I will) but is there any other simple way to do this?
> How to make javafx.scene.Scene resize while maintaining an aspect ratio?
By manipulating the stage size instead of scene:
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Play by resizing the window");
VBox root = new VBox();
root.getChildren().add(btn);
root.setStyle("-fx-background-color: gray");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.minWidthProperty().bind(scene.heightProperty().multiply(2));
primaryStage.minHeightProperty().bind(scene.widthProperty().divide(2));
primaryStage.show();
}
Use removeListener and addListener when changing value ​​to prevent listeners from chaining.
private ChangeListener<? super Number> widthChangeListener;
private ChangeListener<? super Number> heightChangeListener;
#Override
public void start(Stage stage) throws Exception {
widthChangeListener = (observable, oldValue, newValue) -> {
stage.heightProperty().removeListener(heightChangeListener);
stage.setHeight(newValue.doubleValue() / 2.0);
stage.heightProperty().addListener(heightChangeListener);
};
heightChangeListener = (observable, oldValue, newValue) -> {
stage.widthProperty().removeListener(widthChangeListener);
stage.setWidth(newValue.doubleValue() * 2.0);
stage.widthProperty().addListener(widthChangeListener);
};
stage.widthProperty().addListener(widthChangeListener);
stage.heightProperty().addListener(heightChangeListener);
}
In netbeans using Java Fx Scene Builder provide you can use AnchorPane Constraints to set
Resizing of stage.

Resources