CustomResizePolicy not triggering when a column's header's divider is double clicked

452 Views Asked by At

I wrote a custom resize policy for a TableView which is similar to TableView.CONSTRAINED_RESIZE_POLICY in that the total width of visible columns always equals the width of the table itself.

Whenever a column is resized, either by the table being resized or the user dragging the column, the resize policy is called and the columns are resized appropriately.

However, when one of the dividers in the table's header is double-clicked (to "shrink wrap" the column's content) the custom resize policy isn't triggered.

As a result, the total width of the columns can be more or less than the table's width, which is not good.

How can I detect these double clicks and cause my CustomResizePolicy to trigger a call afterwards?

Here is a working example showing the double clicks do not result in a call to the CustomResizePolicy:

import java.util.Locale;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.scene.Parent;
import javafx.scene.Scene;
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> {
    @Override
    public Boolean call ( TableView.ResizeFeatures feature ) {

        System.out.println ( "Called" ); //This does not print when the divider is double-clicked.

        return TableView.CONSTRAINED_RESIZE_POLICY.call( feature );
    }
}
1

There are 1 best solutions below

11
On

This is a bug in TableViewSkin.

In Java 8, the method resizeColumnToFitContent, which is invoked by double clicking a column separator, does not call resizeColumn in TableView. The result is that the layout is not updated correctly. It's enough that you try to resize a fit-to-width column to observe this. In fact, fitting-to-width repeatedly will set the width closer and closer to its real width. Try to fit and then resize several times in a row the same column (doing this with other columns will reset the behavior).

I didn't have time to find a good workaround. There's some semblance of improvement by subclassing TableViewSkin and overriding resizeColumnToFitContent:

class CustomTableSkin<T> extends TableViewSkin<T> {

    public CustomTableSkin(TableView<T> tableView) {
        super(tableView);
        i = tableView.getColumns().size() + 1;
    }

    private int i;

    @Override
    protected void resizeColumnToFitContent(TableColumn<T, ?> tc, int maxRows) {
        double before = tc.getWidth();
        super.resizeColumnToFitContent(tc, maxRows);
        double now = tc.getWidth();
        double diff = before - now;
        i--;
        if (i >= 0)
            resizeColumn(tc, -diff);
    }
}

The addition of calls to resizeColumn help in some cases, but it will take some more research to find out the correct fix.

Java 9 changed the skinning implementation, though I believe the problem is still present there.