4

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 );
    }
}
user1803551
  • 12,965
  • 5
  • 47
  • 74
Grumblesaurus
  • 3,021
  • 3
  • 31
  • 61

1 Answers1

3

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.

user1803551
  • 12,965
  • 5
  • 47
  • 74
  • 1
    I am not able to find CustomTableSkin in java 8. Did I misunderstand your post? – Grumblesaurus Dec 18 '17 at 23:36
  • 1
    Er. I mean I can't find "TableViewSkin" in my java -- openjdk version "1.8.0_151". When I try to "import com.sun.javafx.scene.control.TableViewSkin" I get "The import cannot be resolved. – Grumblesaurus Dec 20 '17 at 08:59
  • @JoshuaD Google should help you with that. – user1803551 Dec 20 '17 at 09:51
  • 1
    I googled it before posting, both times. I will try a third time. In the meantime, if you have the answer, it would be helpful to edit it into your post, as I'm sure I won't be the only person running into that problem, and I promise it is not readily apparent how to use your proposed solution. – Grumblesaurus Dec 20 '17 at 20:06
  • @JoshuaD It depends on on your IDE. – user1803551 Dec 20 '17 at 22:49
  • 1
    I'm looking at the javafx 8 javadoc and I don't see it: https://i.imgur.com/E3oevLH.png, https://docs.oracle.com/javase/8/javafx/api/toc.htm – Grumblesaurus Dec 21 '17 at 02:49
  • @JoshuaD `com.sun` is internal API. The documentation is in the source code. – user1803551 Dec 21 '17 at 17:16
  • I can't import TableViewSkin either. There is no javafx.scene.control.skin package in my Java 8... don't know what that TableViewSkin is and where you got it from? – Spenhouet Jan 07 '18 at 17:34
  • @Spen As I said in the comments above it's in the `com.sun` domain, specifically `com.sun.javafx.scene.control.skin`. – user1803551 Jan 07 '18 at 21:27
  • It seems I don't have access to that in IntelliJ Idea. – Spenhouet Jan 08 '18 at 08:15
  • @Spen You have, you just need to set it in IntelliJ. I don't use it so I don't know how to. As I said above, Google should help you with that. – user1803551 Jan 08 '18 at 22:22