CAD97
CAD97

Reputation: 1313

Dynamic sizing of TabPane based on children

I have a JavaFX hierarchy that basically looks like this when broken down:

Parent // Tested with ScrollPane and AnchorPane, both work this way.
|      // The important part is that TabPane ISN'T the base Node
|      // So its sizing is under its own control, not the Window's
| - TabPane
|   | - Tab
|   |   | - VBox

The contents of the VBox make it dynamically change height. Unfortunately, the TabPane does not automatically respond to re-sizing. I worked out how to get the TabPane to grow here, and later searching revealed this issue on bugs.openjdk which also seems to refer to this.

Unfortunately, there is still an problem here, and it's made clear by giving the TabPane an -fx-background-color. With either the manual/automatic workaround of changing tabs or my workaround to call TabPane#requestLayout, though the TabPane grows properly, it fails to contract.

(NOTE: if you don't tell the VBox to have maxHeight="-Infinity" (USE_PREF_SIZE), the VBox will be the culprit of not decreasing size. However, with this set the VBox will shrink leaving the TabPane bigger than its contents.)

Scenic View reveals that the hierarchy of my test case (below) looks something more like this:

AnchorPane
| - TabPane "tabPane"
|   | - TabContentRegion
|   | - TabContentRegion
|   |   | - VBox "vBox"
|   |   |   | - Button
|   |   |   | - Button
|   | - TabHeaderArea
|   |   | - (Generated Stuff)

When re-sizing the VBox, Scenic View confirms that the VBox's layouBounds/boundsInParent change with both an increase in size and a decrease in size, but the containing TabContentRegion's Bounds only change when the VBox grows larger than the TabContentRegion.

TL;DR: Is there a way (workaround or official) to get a TabPane / TabContentRegion to maintain the same dimensions as its children (take up no extraneous space) even when re-sizing the children?


View the full MCVE here, abridged below:

FXML.fxml

<AnchorPane fx:controller="test.FXMLController"><children>
    <TabPane fx:id="tabPane" style="-fx-background-color:#000000;"><tabs><Tab>
        <VBox fx:id="vBox" maxHeight="-Infinity"><children>
            <Button onAction="#addElement" />
            <Button onAction="#remElement" />
        </children></VBox>
    </Tab></tabs></TabPane>
</children></AnchorPane>

FXMLController.java

@FXML VBox vBox;
@FXML TabPane tabPane;

@FXML void addElement() {
    vBox.getChildren().add(0, new Label("Hello World"));
    tabPane.requestLayout();
}

@FXML void remElement() {
    if (vBox.getChildren().size() > 2) {
        vBox.getChildren().remove(0);
        tabPane.requestLayout(); // Doesn't seem to do anything
    }
}

The test case uses TabPane#requestLayout to get the TabPane to grow but provides two tabs so that you can manually tab between them to see that that doesn't allow the TabPane to shrink either.

Here's another example that's closer to my use case and maybe a bit clearer on what exactly is going on. Here the TabPane is in a ScrollPane, and once the TabPane has extended beyond the ScrollPane's height, even if it should shrink again the ScrollPane still allows the user to scroll down as before, because the TabPane extends down.

Upvotes: 1

Views: 1406

Answers (1)

Naoghuman
Naoghuman

Reputation: 80

in your last example with the TabPane in a ScrollPane I have done following changes:

Add an ChangeListener to the TableView which tells the TabPane when the pref-height is changed:

table.prefHeightProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            tabs.setPrefHeight(table.getPrefHeight() + 75.0d);
        }
    });

Comment out the method calls from requestLayout():

@FXML
void addPerson() {
    table.getItems().add(new Person());
    // updateTabPaneSize(tabs);
}

@FXML
void remPerson() {
    if (table.getItems().size() > 0) {
        table.getItems().remove(0);
        // updateTabPaneSize(tabs);
    }
}

With this changes the height from the TabPane will be update with every changes from your TableView which updates also the ScrollPane (Scroller will shown and removed as needed).

Upvotes: 1

Related Questions