javafxgridpanecolumn-widthhbox

JavaFX - How to fit a GridPane to fill the entire HBox using both scaling width and static width of ColumnConstraints?


I am having issues filling my entire custom HBox with a GridPane that is dynamically created. The HBox exists inside a ListView and already dynamically scales with the ListView without any issues. The issue is that I want some things in my GridPane to scale when expanding the window size with my mouse, but some parts need to stay the same size.

There are two buttons and a filler label that need to stay the same size, which is not an issue. My mock class that summarizes what I am doing:

Controller:

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import static javafx.scene.layout.Region.USE_PREF_SIZE;


public class FXMLDocumentController implements Initializable {

private Label label;
@FXML
private ListView<ConversionJob> listJobs;
@FXML
private Button btnAction;

@FXML
private void handleButtonAction(ActionEvent event) {
    // Generates a new ConversionJob in the ListView.
listJobs.getItems().add(new ConversionJob());

}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // EMPTY
}

public class ConversionJob extends HBox {

    public ConversionJob() {
        super();

        // Creates all elements
        Label lblConversionName = new Label();
        ProgressBar progress = new ProgressBar();
        Button btnPause = new Button();
        Button btnCancel = new Button();
        GridPane grid = new GridPane();

        setGridInfo(grid, lblConversionName, progress, btnPause, btnCancel);

        this.getChildren().addAll(grid);
    }

    private void setGridInfo(GridPane grid, Label lblConversionName, ProgressBar progress, Button btnPause, Button btnCancel) {
        this.setHgrow(grid, Priority.ALWAYS);

    Label filler = new Label();
    filler.setText("  ");

    grid.addColumn(0, filler);
    grid.add(lblConversionName, 1, 0);
    grid.add(progress, 2, 0);
    grid.add(btnPause, 3, 0);
    grid.add(btnCancel, 4, 0);
    grid.setGridLinesVisible(true);


    grid.setMaxWidth(this.getMaxWidth());

    ColumnConstraints col1 = new ColumnConstraints();
    col1.setMinWidth(USE_PREF_SIZE);

    ColumnConstraints col2 = new ColumnConstraints();
    col2.setPercentWidth(40);

    ColumnConstraints col3 = new ColumnConstraints();
    col3.setPercentWidth(40);

    ColumnConstraints col4 = new ColumnConstraints();
    col4.setMinWidth(USE_PREF_SIZE);
    col4.setMaxWidth(USE_PREF_SIZE);

    ColumnConstraints col5 = new ColumnConstraints();
    col5.setMinWidth(USE_PREF_SIZE);
    col5.setMaxWidth(USE_PREF_SIZE);

    grid.getColumnConstraints().addAll(col1, col2, col3, col4, col5);
    }
}
}

FXML document:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="500.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testieclass.FXMLDocumentController">
   <children>
      <ListView fx:id="listJobs" layoutX="54.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="40.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
      <Button fx:id="btnAction" layoutX="53.0" layoutY="160.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Button" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="460.0" />
   </children>
</AnchorPane>

I get that I am supposed to somehow use the ColumnConstraits class for this, but I want the first label not to get larger than it's max / preferred width, and the same goes for the last two buttons. The lblConversionName and progress objects should however be expanding the GridPane so that it fills the width of the HBox the GridPane exists in.

So basically, what I want is:

Changing the numbers in col.setPercentageWidth(RandomNumber) feels completely random since sometimes it completely goes off-screen with my HBox and sometimes it doesn't scale the percentages at all. Does anyone know how to solve my problem?


Solution

  • Since the 2 resizable columns should grow to the same size, you can simply work with the hgrow properties and assign Priority.NEVER and Priority.ALWAYS to the columns that should not be resized and those that should be resized respectively:

    GridPane grid = new GridPane();
    
    ColumnConstraints neverGrow = new ColumnConstraints();
    neverGrow.setHgrow(Priority.NEVER);
    
    ColumnConstraints alwaysGrow = new ColumnConstraints();
    alwaysGrow.setHgrow(Priority.ALWAYS);
    
    grid.getColumnConstraints().addAll(
            neverGrow,
            alwaysGrow,
            alwaysGrow,
            neverGrow,
            neverGrow);
    
    // just use the most basic resizable nodes to demonstrate this
    Region region1 = new Region();
    region1.setStyle("-fx-background-color: red;");
    region1.setMinHeight(50);
    Region region2 = new Region();
    region2.setStyle("-fx-background-color: blue;");
    region2.setMinHeight(50);
    
    // fill the first row with some nodes
    grid.addRow(0,
            new Label("Some Text:"),
            region1,
            region2,
            new Button("Click me"),
            new Button("me too"));