javajavafxtableviewjavafx-tableview

How to spread the total width amongst all columns on table construction?"


I am trying to write a custom resize policy that acts like TableView.CONSTRAINED_RESIZE_POLICY in that it set the total width of all visible to columns to the total available width in the table, so that the horizontal scroll bar never appears.

I am using the line double widthAvailable = table.getWidth() - getScrollbarWidth(table); to try do to this (see full code below).

Unfortunately, this calculation seems to return 4 too many. My guess is that there is some other thing I am also supposed to be subtracting from the table width that I am missing, which happens to be 4 pixels wide in the default JavaFX theme.

Here is a complete program demonstrating the problem:

package net.joshuad.hypnos.test;

import java.util.List;
import java.util.Locale;
import java.util.Set;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class CustomResizeExample extends Application {

    @SuppressWarnings("unchecked")
    private Parent getContent () {
        TableView <Locale> table = new TableView <>( FXCollections.observableArrayList( Locale.getAvailableLocales() ) );
        TableColumn <Locale, String> countryCode = new TableColumn <>( "CountryCode" );
        countryCode.setCellValueFactory( new PropertyValueFactory <>( "country" ) );
        TableColumn <Locale, String> language = new TableColumn <>( "Language" );
        language.setCellValueFactory( new PropertyValueFactory <>( "language" ) );
        table.getColumns().addAll( countryCode, language );

        TableColumn <Locale, Locale> local = new TableColumn <>( "Locale" );
        local.setCellValueFactory( c -> new SimpleObjectProperty <>( c.getValue() ) );

        table.getColumns().addAll( local );
        table.setColumnResizePolicy( new CustomResizePolicy() );

        BorderPane pane = new BorderPane( table );
        return pane;
    }
    @Override
    public void start ( Stage stage ) throws Exception {
        stage.setScene( new Scene( getContent(), 800, 400 ) );
        stage.show();
    }

    public static void main ( String[] args ) {
        launch ( args );
    }

}

@SuppressWarnings ( "rawtypes" ) 
class CustomResizePolicy implements Callback <TableView.ResizeFeatures, Boolean> {

    @SuppressWarnings("unchecked")
    @Override
    public Boolean call ( TableView.ResizeFeatures feature ) {

        TableView table = feature.getTable();
        List <TableColumn> columns = table.getVisibleLeafColumns();
        double widthAvailable = table.getWidth() - getScrollbarWidth ( table );


        double forEachColumn = widthAvailable / columns.size();

        for ( TableColumn column : columns ) {
            column.setMinWidth( forEachColumn );
            column.setMaxWidth( forEachColumn );
        }

        return true;

    }

    private double getScrollbarWidth ( TableView table ) {
        double scrollBarWidth = 0;
        Set <Node> nodes = table.lookupAll( ".scroll-bar" );
        for ( final Node node : nodes ) {
            if ( node instanceof ScrollBar ) {
                ScrollBar sb = (ScrollBar) node;
                if ( sb.getOrientation() == Orientation.VERTICAL ) {
                    if ( sb.isVisible() ) {
                        scrollBarWidth = sb.getWidth();
                    }
                }
            }
        }

        return scrollBarWidth;
    }
}

See how the table is a little wider than it ought to be? I would like to be able to set the columns' width such that no horizontal scrollbar appears.

enter image description here


Solution

  • Scenic View is a useful tool to get acquainted with. It gives you many details about the scene graph. The space you are looking for is clipped-container:

    enter image description here

    enter image description here

    Note that setting a CustomResizePolicy the way you did effectively does not allow columns to be resized as they are always allocated an equal share of available space.

    Here is the calculation you are probably looking for:

    Region region = null;
    Set<Node> nodes = table.lookupAll(".clipped-container");
    for (Node node : nodes) {
        if (node instanceof Region) {
            region = (Region) node;
        }
    }
    for (TableColumn<Locale, ?> column : table.getColumns()) {
        column.setPrefWidth(region.getWidth() / table.getColumns().size());
    }
    

    I don't completely understand why CONSTRAINED_RESIZE_POLICY is not what you want, as it divides the space equally and does not allow resizing to exceed the allocated space (thus a horizontal scrollbar does not appear), but if you're making some special resize policy the above should help.