fge
fge

Reputation: 121702

Binding a TableColumn's width to the width of the header or content whichever is higher?

The FXML for the TableView is as such:

<TableView fx:id="invocationStatsTable">
    <columns>
        <TableColumn fx:id="ruleName" text="Rule"/>
        <TableColumn fx:id="ruleClass" text="Rule class"/>
        <TableColumn fx:id="ruleType" text="Rule type"/>
        <TableColumn fx:id="nrCalls" text="Invocations"
            sortType="DESCENDING"/>
        <TableColumn fx:id="callDetail" text="N / E / F (*)"
            sortable="false"/>
        <TableColumn fx:id="callGraph" text="Graph" sortable="false"
            prefWidth="200.0"/>
    </columns>
</TableView>

which gives this (bottom):

the result

The problem is immediately, well, visible: columns are not sized correctly.

And I'd like to avoid having to set prefWidth by hand on all columns since

  1. it will always be a guess game and at some point the guess will be wrong, and
  2. it's time consuming.

I'd much rather the resize were automatic AND bound to the maximum width or either the column header or any data within, if possible...

For now, here is the code, first of the column initialization in the StatsTabDisplay class (which is the "JavaFX controller"):

@Override
public void init()
{
    setColumnValue(ruleName, r -> r.getRuleInfo().getName());
    setColumnValue(ruleClass, r -> r.getRuleInfo().getClassName());
    setColumnValue(ruleType, r -> r.getRuleInfo().getType());
    setColumnValue(nrCalls, r -> r.getEmptyMatches() + r.getFailedMatches()
        + r.getNonEmptyMatches());
    setColumnValue(callDetail, r -> String.format("%d / %d / %d",
        r.getNonEmptyMatches(), r.getEmptyMatches(), r.getFailedMatches()));
    setColumnValue(callGraph, Function.identity());
    callGraph.setCellFactory(CallGraphTableCell::new);
}

The setColumnValue() method is defined in a utility class:

public static <S, T> void setColumnValue(final TableColumn<S, T> column,
    final Function<? super S, ? extends T> f)
{
    column.setCellValueFactory(
        param -> new SimpleObjectProperty<T>()
        {
            @Override
            public T get()
            {
                return f.apply(param.getValue());
            }
        }
    );
}

The code to trigger the table refresh is:

public void handleRefreshInvocationStatistics()
{
    final boolean complete = model.isLoadComplete();

    taskRunner.computeOrFail(
        view::disableTableRefresh,
        model::getRuleInvocationStatistics,
        stats -> view.displayRuleInvocationStatistics(complete, stats),
        throwable -> mainView.showError("Load error",
            "Unable to load matcher statistics", throwable)
    );
}

and the matching code fragments in the view is this (the display member of the view is the instance of StatsTabDisplay):

@Override
public void displayRuleInvocationStatistics(final boolean complete,
    final List<RuleInvocationStatistics> stats)
{
    if (complete) {
        display.completionStatus.setVisible(false);
        display.tableRefresh.setVisible(false);
    }

    display.invocationStatsTable.getSortOrder().setAll(display.nrCalls);
    display.invocationStatsTable.getItems().setAll(stats);
    display.invocationStatsTable.sort();
    display.tableRefresh.setDisable(false);
}

@Override
public void disableTableRefresh()
{
    display.tableRefresh.setDisable(true);
}

It is possible to achieve the required feature?


EDIT well, I know it is definitely possible, since when you double click on the "right of the column header" (I don't know the exact term for it) it achieves exactly what I want; except that I'd like it to be in code, not requiring a user interaction...

Upvotes: 0

Views: 129

Answers (1)

eckig
eckig

Reputation: 11134

I would suggest making use of the columnResizePolicy.

If you set tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY) all columns will be equally resized until the TableViews maximum width is reached. But you can write your own policy:

The policy is just a Callback where ResizeFeatures is given as input from where you have access to the TableColumn.

Upvotes: 2

Related Questions