I have a JavaFX BorderPane, inside of which there is a GridPane for holding contents. The GridPane consists of two columns, one for a Text with a Label (both in a VBox) and one for a Button. The text column is supposed to be 80% width and the button column is supposed to be 20% width. When I set the text of the Text and it is wider than the column, it wraps to the width of the VBox. However, when I change the size of the Window to make it wider and then shrink the Window back the text in the Text does not shrink back.
You can see my attempts at limiting the width in the wireListeners function of TestMainView. I have tried different combos of things but have gotten nothing to work.
Here is my code:
Launcher:
package com.test.app;
import com.test.app.view.TestMainView;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TestGuiApplication extends Application {
#Override
public void start(Stage initialStage) throws Exception {
TestMainView mainView = new TestMainView();
initialStage.setScene(new Scene(mainView.getView(), 300, 300));
mainView.init();
initialStage.show();
}
public static void main(String args[]){
launch(args);
}
}
GUI:
package com.test.app.view;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
public class TestMainView {
private BorderPane borderPane;
private GridPane gridPane;
private VBox viewBox;
private VBox selectedItemsColumn;
private Label selectedItemsTitleLabel;
private Text selectedItemsList;
private Button goButton;
public TestMainView() {
this.borderPane = new BorderPane();
}
public void init() {
createView();
wireListeners();
}
public BorderPane createView(){
borderPane.setCenter(createCenterView());
return borderPane;
}
public void wireListeners(){
goButton.setOnMouseClicked((event) -> {
selectedItemsList.setText(selectedItemsList.getText() + "- X X X X X X");
});
//Tried the following listeners to prevent the text from growing otuside column size:
//Keep border pane width matched to Scene
borderPane.prefHeightProperty().bind(borderPane.getScene().heightProperty());
borderPane.prefWidthProperty().bind(borderPane.getScene().widthProperty());
//Set text wrapping width to bind to its VBox, this worked
selectedItemsList.wrappingWidthProperty().bind(selectedItemsColumn.widthProperty());
//Set VBox width to match the gridpane column width
selectedItemsColumn.prefWidthProperty().bind(gridPane.getColumnConstraints().get(0).percentWidthProperty());
}
public VBox createCenterView(){
viewBox = new VBox();
viewBox.getChildren().add(createGridView());
return viewBox;
}
public GridPane createGridView(){
gridPane = new GridPane();
selectedItemsColumn = new VBox();
selectedItemsTitleLabel = new Label("Selected items:");
selectedItemsList = new Text("X X X X X X");
selectedItemsColumn.getChildren().addAll(selectedItemsTitleLabel, selectedItemsList);
gridPane.add(selectedItemsColumn, 0, 0);
goButton = new Button("Go!");
gridPane.add(goButton, 1, 0);
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(80);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(20);
gridPane.getColumnConstraints().addAll(col1, col2);
gridPane.setHgap(10);
gridPane.setVgap(10);
gridPane.setPadding(new Insets(10,10,10,10));
gridPane.setGridLinesVisible(true);
return gridPane;
}
public Parent getView(){
return borderPane;
}
}
How can I get the Text contents of my VBox to match their width to that of the GridPane column and not overflow the column?
Related
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.
I'm having some difficulty with ScrollPane in JavaFX 8 showing the scrollbar as needed. What I'm currently doing is simply creating a FlowPane with x number of elements, and setting that as the content of the ScrollPane.
The problem happens when I shrink down perpendicular to the orientation of the FlowPane. When elements begin to wrap and go out of bounds, the scrollbar does not appear. This does not happen when I shrink parallel to the orientation. I have a small Java program to exemplify the issue.
Start
Shrinking Parallel
Shrinking Perpendicular
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane();
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
scroll.setStyle("-fx-border-color: green");
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for(int i = 0; i < panes; i++) {
StackPane filler = new StackPane();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
Have a look at the code below and tell me if that's what you want to achieve. I am still not sure what cause the problem, I will have to look the documentation of ScrollPane to find out. My suspicion is at setFitToWidth & setFitToHeight methods. Although I still believe it's not a bug.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane();
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
scroll.setStyle("-fx-border-color: green");
// Apparently this cause the issue here.
// scroll.setFitToHeight(true);
// scroll.setFitToWidth(true);
// Instead just make the flow pane take the dimensions of the ScrollPane
// the -5 is to not show the Bars when both of panes have the same dimensions
flow.prefWidthProperty().bind(Bindings.add(-5, scroll.widthProperty()));
flow.prefHeightProperty().bind(Bindings.add(-5, scroll.heightProperty()));
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for (int i = 0; i < panes; i++) {
HBox filler = new HBox();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
Looking documentation of the ScrollPane, and in specific the setFitToHeight you will find that :
Property description:
If true and if the contained node is a
Resizable, then the node will be kept resized to match the height of
the ScrollPane's viewport. If the contained node is not a Resizable,
this value is ignored.
And because the node inside the ScrollPane will be kept resized to match the width and height of the ScrollPane's viewport thats why the Vertical ScrollBar will never appear.
You can add the code below to always show your vertical scrollbar.
scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
When the required height of the FlowPane inside the ScrollPane is calculated a width value of -1 is passed. The flow pane will then report the height required when all its content fits into a single line.
As a workaround you could pass the width from the last layout calculation in this case.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane() {
#Override protected double computeMinHeight(double width) {
double minHeight = super.computeMinHeight(width != -1 ? width :
/* When no width is specified, use the current contol size*/
getWidth());
return minHeight;
}
};
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
flow.maxWidthProperty().bind(scroll.widthProperty());
scroll.widthProperty().addListener((observable, oldValue, newValue)->{
/* clearSizeCache */
flow.requestLayout();
});
scroll.setStyle("-fx-border-color: green");
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for(int i = 0; i < panes; i++) {
StackPane filler = new StackPane();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
I'm trying to increase my knowledge on javafx, but running into some troubles with the controls. They are often not wide enough and say ... instead of a desired string. I've attempted to use the setWidth method but this does not work. In this specific case I'm referring to choiceboxes.
This is a standard javafx program, and I've done this code in the start method. The choicebox is inside of a GridPane. Here's a sample code that recreates the issue.
//imports
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.*;
//main class
public class HelloWorld extends Application {
//main method
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
//set title for window
primaryStage.setTitle("Hello World!");
//create a new button & format it
Button btn = new Button();
btn.setText("Say 'Hello World'");
//give button a set action (print hello world)
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
//create gridpane to hold button
GridPane root = new GridPane();
//establish gridpane
for(int x = 0; x < 20; x++){
root.getRowConstraints().add(new RowConstraints(30));
}
for(int x = 0; x < 30; x++){
root.getColumnConstraints().add(new ColumnConstraints(20));
}
//set constraints, add button to gridpane
root.setConstraints(btn,3,3);
root.getChildren().add(btn);
//set scene and show
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
If you are adding this node to a GridPane, you probably need to extend the node across multiply columns. JavaDocs
add(Node child, int columnIndex, int rowIndex, int colspan, int rowspan)
Adds a child to the gridpane at the specified column,row position and spans.
gridPane.add(accounts, 0, 0, 2, 1);
First, code that generates a UI that illustrates the problem:
package test;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(final String[] args) throws Exception {
launch(args);
}
#Override
public void start(final Stage window) throws Exception {
// Create a VBox to hold the table and button
final GridPane root = new GridPane();
root.setHgap(5);
root.setVgap(5);
// Add a combo-box to the first row
final ComboBox<String> dropdown1 = new ComboBox<>();
dropdown1.getItems().add("Option 1");
dropdown1.getSelectionModel().selectFirst();
root.add(dropdown1, 0, 0);
// Add a checkbox to the first row
final CheckBox checkbox1 = new CheckBox("CB Text 1");
root.add(checkbox1, 1, 0);
// Add a combo-box to the second row
final ComboBox<String> dropdown2 = new ComboBox<>();
dropdown2.getItems().add("Option 2");
dropdown2.getSelectionModel().selectFirst();
root.add(dropdown2, 0, 1);
// Add a checkbox, wrapped in an HBox, to the second row
final CheckBox checkbox2 = new CheckBox("CB Text 2");
final HBox hbox = new HBox(checkbox2);
hbox.setAlignment(Pos.BASELINE_LEFT);
root.add(hbox, 1, 1);
GridPane.setValignment(hbox, VPos.BASELINE);
// Show the JavaFX window
final Scene scene = new Scene(root);
window.setScene(scene);
window.show();
}
}
The above code generates the following UI (Java 8u102 Windows x64):
As shown in the image, the vertical alignment of the CheckBox in the second row is misaligned with the ComboBox. I expect everything to be aligned on the text baseline. How can I get the second row in the GridPane to match the alignment of the first row, without removing the HBox?
Modify the code that populates the offending cell to be the following:
// Add a checkbox, wrapped in an HBox, to the second row
final CheckBox checkbox2 = new CheckBox("CB Text 2");
final HBox hbox = new HBox(checkbox2);
hbox.setFillHeight(true); // Added this
hbox.setAlignment(Pos.CENTER_LEFT);// Changed the alignment to center-left
root.add(hbox, 1, 1);
//GridPane.setValignment(hbox, VPos.BASELINE); This is unnecessary
This code will force the HBox to be the same height as the row, then vertically center the CheckBox within it.
When I translate a node outside of the bounds of it's parent. The minimum size of the parent of the parent is set to it's current size. You can see it with this demo:
package com.neonorb.test;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.io.IOException;
/**
* Created by chris on 7/20/15.
*/
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
Label label = new Label("translating label");
Label markerLabel = new Label("marker label");
Button button = new Button("button");
VBox leftSpace = new VBox();
Label leftLabel = new Label("left space");
leftSpace.getChildren().add(leftLabel);
Rectangle rectangle = new Rectangle();
rectangle.setFill(Color.RED);
rectangle.heightProperty().bind(leftSpace.heightProperty());
rectangle.widthProperty().bind(leftSpace.widthProperty());
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
new Thread() {
public void run() {
Platform.runLater(() -> label.setTranslateY(1000.0));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() -> label.setTranslateY(0.0));
}
}.start();
}
});
BorderPane borderPane = new BorderPane();
BorderPane center = new BorderPane();
center.setCenter(label);
center.setBottom(markerLabel);
borderPane.setCenter(center);
borderPane.setTop(button);
borderPane.setLeft(leftSpace);
borderPane.setRight(rectangle);
primaryStage.setScene(new Scene(borderPane));
primaryStage.show();
}
}
The reason for the side bar things (the VBox and Rectangle) is because they exist in my real application. The VBox just holds more content, and the Rectangle is there to keep the center components centered (normally transparent, but here it is colored for visibility). As you can see, the width and height of the rectangle are binded to the VBox's height:
rectangle.heightProperty().bind(leftSpace.heightProperty());
rectangle.widthProperty().bind(leftSpace.widthProperty());
To reproduce the problem, you can increase the height of the window a little (about an inch), then hit the button. The node will be translated down 1000 pixels and back. Now try to shrink the window, the text at the bottom, ("marker label"), will start to be hidden by the bottom of the window.
I fixed it by using a Region instead of a Rectangle and setting it's preferred size.