wrap content in JavaFX - java

package example;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Text text = new Text("This is a Text");
VBox box = new VBox();
box.setAlignment(Pos.CENTER);
box.setStyle("-fx-background-color: yellow;");
box.getChildren().add(text);
StackPane container = new StackPane();
container.getChildren().add(box);
BorderPane bp = new BorderPane();
bp.setCenter(container);
Scene scene = new Scene(bp, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Here's the output:
Question: Can someone explain to me why the Vbox fill the whole screen? Is there a method that is similar to Android's wrap_content? I want the image below to be the output:

Solution
Wrap the VBox in a Group; e.g. use:
container.getChildren().add(new Group(box));
instead of:
container.getChildren().add(box);
Why it works
From the Group javadoc:
By default, a Group will "auto-size" its managed resizable children to their preferred sizes during the layout pass.
This means that the VBox won't grow past the preferred size of it's content (which is just enough area to display the label inside it).
Alternate implementation
Set the maximum size of the VBox to the preferred size. Then the VBox will only ever grow large enough to fit the preferred size of the content inside it and will never grow any larger.
box.setMaxSize(VBox.USE_PREF_SIZE, VBox.USE_PREF_SIZE);
Why VBox grows by default
It is a resizable container which will stretch to fill available area.
Note
I don't know that the effect is exactly the same as an Android wrap_content method as I have never developed for Android, however the effect does seem to exactly match the second image you provided in your question, which appears to be what you want.

VBox automatically resizes itself to the size of the Parent, so it is better not to set background color to it. Instead, you can use a Label in place of a Text and then add background color to the Label instead of the VBox.
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Label text = new Label("This is a Text");
VBox box = new VBox();
box.setAlignment(Pos.CENTER);
text.setStyle("-fx-background-color: yellow;");
box.getChildren().add(text);
StackPane container = new StackPane();
container.getChildren().add(box);
BorderPane bp = new BorderPane();
bp.setCenter(container);
Scene scene = new Scene(bp, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
This will give you an output like an image below:

Related

JavaFX - BorderPane is not centered in scene/stage

I am currently coding a board game and I am using java and for GUI purposes javaFX. The game requires for a map (the game board) to be in the middle of the screen and various options and additional information to be around it. My idea was to have a BorderPane where the center node is the game board and top, bottom etc are the additional options.
My problem is that upon starting the App the BorderPane is not centered in the stage but is slightly extended to the right and botton, where it is not visible. Thus my bottom Node can't be seen. Strangely if I minimize the window and maximize it again everything is where it should be and perfectly inside the bounds of the stage.
The application before minimizing and maximizing again
And afterwards (The way it should look like from the beginning)
My center Node is a normal Pane. Also I do stage.setMaximize(true). So the window is already maximized upon starting the application and it should not make a difference to minimze and maximize again.
The code for this scene essentially boils down to this:
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
Pane pane = createGameBoard();
pane.setId("mapPane");
Button button = new Button("Save");
VBox box = new VBox(0, button);
box.setAlignment(Pos.CENTER);
Label label = new Label("Bottom");
label.setPrefHeight(20);
BorderPane borderPane = new BorderPane();
borderPane.setTop(top);
borderPane.setBottom(bottom);
borderPane.setCenter(center);
borderPane.setPadding(new Insets(10));
Scene scene = new Scene(borderPane);
scene.getStylesheets().add(Objects.requireNonNull(MainGUI.class.getResource("/game.css")).toString());
stage.setScene(scene);
}
The game.css stylesheet only sets a background colour at the moment.
I am using: Java 17, JavaFX 17.
If you need any further information I am happy to provide it :)
Thanks!
Edit:
To reproduce my problem run this code. The issue should appear once you click on the button "Next Screen".
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class App extends Application {
private Stage stage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setTitle("App");
stage.setMinHeight(600);
stage.setMinWidth(800);
stage.setWidth(java.awt.Toolkit.getDefaultToolkit().getScreenSize().getWidth());
stage.setHeight(java.awt.Toolkit.getDefaultToolkit().getScreenSize().getHeight());
stage.setMaximized(true);
stage.setOnCloseRequest(e -> System.exit(0));
Button button = new Button("Next Screen");
button.setOnAction(e -> {gameScreen();});
BorderPane borderPane = new BorderPane();
borderPane.setCenter(button);
Scene scene = new Scene(borderPane);
stage.setScene(scene);
stage.show();
}
private void gameScreen() {
Circle circle = new Circle(0, 0, 4);
Pane pane = new Pane(circle);
pane.setStyle("-fx-background-color: #00c3ff");
Button button = new Button("Top");
VBox vBox = new VBox(0, button);
vBox.setAlignment(Pos.CENTER);
Label label = new Label("Bottom");
BorderPane borderPane = new BorderPane();
borderPane.setTop(vBox);
borderPane.setCenter(pane);
borderPane.setBottom(label);
borderPane.setPadding(new Insets(10));
Scene scene = new Scene(borderPane);
stage.setScene(scene);
}
}
My problem was resolved by changing the root of the scene I was using and not create a new scene everytime I switch the layout (e.g. from main menu to the game screen).

Including scale factor in layoutBounds in JavaFX

I'm using JavaFx 8.
From the JavaDoc of javafx.scene.Node.scaleYProperty():
[...] This scale factor is not included in layoutBounds by default, which makes it ideal for scaling the entire node after all effects and transforms have been taken into account. [...]
How can I include the scaling factor in layoutBounds, though?
Some context:
In the following example, when pressing the button I would like the GridPane to react also to the scaling of the HBox whithout having to hardcode the prefHeight of the RowConstraints.
Being able to include the scaling factor into the layoutBounds probably would do the trick, but other solutions are welcome as well.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class ScalingStandAlone extends Application {
private VBox vBox = new VBox();
private GridPane gridPane = new GridPane();
private HBox hBox = new HBox();
private ToggleButton button = new ToggleButton("Click to scale");
private Label firstRowLabel = new Label("Some content in text form");
private Label secondRowLabel = new Label("Some content for scaling");
private Label thirdRowLabel = new Label("Some moving content");
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
AnchorPane.setTopAnchor(vBox, 5.);
AnchorPane.setLeftAnchor(vBox, 5.);
AnchorPane.setRightAnchor(vBox, 5.);
root.autosize();
Scene scene = new Scene(root);
stage.setTitle("GridRow Scale Demo");
stage.setWidth(400);
stage.setHeight(300);
stage.setScene(scene);
stage.show();
root.getChildren().add(vBox);
vBox.setAlignment(Pos.CENTER);
vBox.getChildren().add(gridPane);
vBox.getChildren().add(button);
vBox.setStyle("-fx-spacing: 15;");
configureGridPane(root);
button.setOnAction(event -> {
hBox.setScaleY(button.isSelected() ? 2 : 1);
});
}
private void configureGridPane(Pane root) {
hBox.getChildren().add(secondRowLabel);
// Styling //
firstRowLabel.setStyle("-fx-padding: 5;");
hBox.setStyle("-fx-background-color: #800000; -fx-padding: 5;");
secondRowLabel.setStyle("-fx-text-fill: white; -fx-padding: 5;");
thirdRowLabel.setStyle("-fx-padding: 5;");
gridPane.add(firstRowLabel, 0, 0);
gridPane.add(hBox, 0, 1);
gridPane.add(thirdRowLabel, 0, 2);
gridPane.setGridLinesVisible(true);
gridPane.getColumnConstraints().add(new ColumnConstraints());
gridPane.getColumnConstraints().get(0).setPercentWidth(100);
}
}
From the Javadocs for Group:
Any transform, effect, or state applied to a Group will be applied to
all children of that group. Such transforms and effects will NOT be
included in this Group's layout bounds, however if transforms and
effects are set directly on children of this Group, those will be
included in this Group's layout bounds.
(my emphasis added).
Therefore, if you simply wrap your HBox in a Group, you will achieve the desired effect:
// gridPane.add(hBox, 0, 1);
gridPane.add(new Group(hBox), 0, 1);

How to make left-aligned TextFlow in the center of the window in JavaFX

How to make text in TextFlow justify left, but the TextFlow is in the center of the window?
I try to implement it with VBox, StackPane and BorderPane, but they can only align the text in the center, or make the TextFlow to the left of the window.
The effect I need is similar to IDEA:
But the effect I achieved is like this:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TextFlow text = new TextFlow(
new Text("Search Everywhere\n"),
new Text("Project View\n"),
new Text("Go to File\n")
);
text.setTextAlignment(TextAlignment.LEFT);
VBox root = new VBox(text);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
}
Thanks for c0der tips, I found FlowPane can easily achieve this effect:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TextFlow text = new TextFlow(
new Text("Search Everywhere\n"),
new Text("Project View\n"),
new Text("Go to File\n")
);
text.setTextAlignment(TextAlignment.LEFT);
FlowPane root = new FlowPane(text);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
}
I am not sure this is exactly what you are looking for, but should put you in the right direction. I added color so you can see where one control ends and the other continues.
To make this work you need to ensure that your TextFlow doesn't size bigger than what you want, otherwise it will not give you the expected behavior. In this instance I choose 200x200 and you will see it center in the window.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class Sample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
TextFlow tf = new TextFlow();
tf.setStyle("-fx-background-color: red;");
tf.setTextAlignment(TextAlignment.LEFT);
tf.setMaxSize(200, 200);
StackPane sp = new StackPane(tf);
sp.setStyle("-fx-background-color: blue;");
Text t1 = new Text("This is line one, left justified" + System.lineSeparator());
Text t2 = new Text("This is line two, left justified"+ System.lineSeparator());
Text t3 = new Text("This is line three, left justified"+ System.lineSeparator());
Text t4 = new Text("This is line four, left justified"+ System.lineSeparator());
tf.getChildren().addAll(t1, t2, t3, t4);
Scene scene = new Scene(sp);
primaryStage.setScene(scene);
primaryStage.setWidth(600);
primaryStage.setHeight(600);
primaryStage.show();
}
}

JavaFX not showing full web page on 16:9 ratio

Heres the code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args){
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception{
WebView myWebView = new WebView();
WebEngine engine = myWebView.getEngine();
engine.load("https://twitter.com/");
VBox root = new VBox();
root.getChildren().addAll(myWebView);
Scene scene = new Scene(root, 1280, 720);
stage.setScene(scene);
stage.show();
}
}
If I run this it will show the web page on the top 3 quarters of the application window with a white box taking up the other quarter. It's even worse if I maximise the window, the site only takes up about half of the application window. Not sure whats causing this...
You will need to make it fill the entire space on the Stage. Depending on your choice you will have the above cases.
Case 1 : Using a BorderPane.
By adding the BorderPane directly to the Stage and the WebView on the center of the BorderPane it will make the webView take all the available width and height of the Stage.
Example :
#Override
public void start(Stage stage) throws Exception {
WebView myWebView = new WebView();
WebEngine engine = myWebView.getEngine();
engine.load("https://twitter.com/");
BorderPane root = new BorderPane();
root.setCenter(myWebView);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Case 2 : Using a VBox or HBox.
Those panes takes by default all the available width for VBox and height for HBox space and left the other dimension to be calculated by their children nodes. In order to utilize the full space you need to tell the node inside those panes to always grow ( vertical or horizontal depending of the pane )
Example :
#Override
public void start(Stage stage) throws Exception {
WebView myWebView = new WebView();
WebEngine engine = myWebView.getEngine();
engine.load("https://twitter.com/");
VBox root = new VBox(); // or HBox
root.getChildren().add(myWebView);
// HBox.setHgrow(myWebView, Priority.ALWAYS);
VBox.setVgrow(myWebView, Priority.ALWAYS);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
There are a lot of other panes as well like AnchorPane ( which you will need to set its anchors ) or FlowPane etc and in some cases you will have to bind their prefWidthProperty and prefHeightProperty with the actual widthProperty/heightProperty of the Stage. But I believe you got the point.

How to set the coordinate for my button in JavaFX?

public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
ScrollBar sc = new ScrollBar();
sc.setMin(0);
sc.setMax(100);
Button btn = new Button();
btn.relocate(150, 100);
btn.setText("Launch Keyboard lights");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
root.getChildren().add(sc);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
I tried using btn.setXlayout and y and it also didn't work. I think its because of the root of (new Scene(root, 200, 250)); but what am I supposed to write to get the button into a new position?
A StackPane is probably not what you want here - it is used to stack controls on top of one another (z-axis), but the controls themselves are positioned according to the Alignment property of the StackPane.
You are probably looking for something like AnchorPane, which allows you to anchor controls to one or more sides of the pane. Alternatively, you can use one of the many other layout panes available in JavaFX, depending on what layout you are looking to achieve.
Take a look at this tutorial to get an idea of the various layout panes available. Also, you may want to play around with Scene Builder at least for prototyping.
A StackPane is a layout pane, which means it organizes the layout of its child nodes for you. (By default it centers them, though you can change that so that it positions them in any of the alignments specified by Pos.)
The recommendation is that you use a layout pane, or combination of layout panes, that manage the child nodes in the way you want. If you really want to position them yourself (really, you don't), you can use a Pane as the root instead of a StackPane, and then use btn.setLayoutX and btn.setLayoutY(...)
Try this
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.deneme
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class MainApp extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
ScrollBar sc = new ScrollBar();
sc.setMin(0);
sc.setMax(100);
Pane pane = new Pane();
Button btn = new Button();
pane.setPrefSize(300,250);
btn.setText("Launch Keyboard lights");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("asda");
}
});
StackPane root = new StackPane();
pane.getChildren().add(btn);
pane.getChildren().add(sc);
btn.setLayoutX(50); //this is how you change locations
btn.setLayoutY(50); //this is how you change locations
root.getChildren().add(pane);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Categories

Resources