Fix for JDK-8089009 : TableView with CONSTRAINED_RESIZE_POLICY incorrectly displays a horizontal scroll bar.

dandem sai pradeep saipradeep.dandem at gmail.com
Mon Jul 25 09:43:22 UTC 2022


I have investigated the root cause of the issue “*JDK-8089009 : TableView
with CONSTRAINED_RESIZE_POLICY incorrectly displays a horizontal scroll bar*”
and have a fix for it.

Before I get to the actual solution, let me briefly explain the issue and
root cause.



*Issue:*

When the TableView is set with built-in CONSTRAINED_RESIZE_POLICY, the
horizontal scroll bar keeps flickering when the tableView width is reduced.



*Cause:*

The table columns widths are recalculated only if the difference of the
tableView width and the total columns width is greater than 1px. Because of
these improper calculations, if the tableView width is reduced by 1px, the
column widths are not updated. Which results in computing for hBar
visibility in VirtualFlow to improper results and letting the hBar visible.
Whereas if the tableView width is reduced by more than 1px, the
calculations are done correctly and the hBar visibility is turned off.
Because of this behaviour, it looks like the hBar is flickering when the
tableView width is reduced (let’s say by dragging).



To confirm this behaviour, please find the below example that showcases the
issue:

When the tableView width is reduced/increased by 1px, the column widths are
recalculated only after every alternate 1px change. Whereas if the
tableView width is reduced/increased by more than 1px, the column widths
are calculated correctly.



import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ConstrainedResizePolicyIssueDemo extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        TableColumn<Person, String> fnCol = new TableColumn<>("First Name");
        fnCol.setMinWidth(100);
        fnCol.setCellValueFactory(param ->
param.getValue().firstNameProperty());

        TableColumn<Person, String> priceCol = new TableColumn<>("Price");
        priceCol.setMinWidth(100);
        priceCol.setMaxWidth(150);
        priceCol.setCellValueFactory(param ->
param.getValue().priceProperty());

        TableColumn<Person, String> totalCol = new TableColumn<>("Total");
        totalCol.setMinWidth(100);
        totalCol.setMaxWidth(150);
        totalCol.setCellValueFactory(param ->
param.getValue().totalProperty());

        ObservableList<Person> persons = FXCollections.*observableArrayList*
();
        persons.add(new Person("Harry", "200.00", "210.00"));
        persons.add(new Person("Jacob", "260.00", "260.00"));

        TableView<Person> tableView = new TableView<>();
        tableView.getColumns().addAll(fnCol, priceCol, totalCol);
        tableView.setItems(persons);
        tableView.setColumnResizePolicy(TableView.
*CONSTRAINED_RESIZE_POLICY*);
        tableView.setMinWidth(500);
        tableView.maxWidthProperty().bind(tableView.minWidthProperty());

        Button button1 = new Button("Reduce 1px");
        button1.setOnAction(e -> tableView.setMinWidth(tableView.getMinWidth()
- 1));
        Button button2 = new Button("Reduce 10px");
        button2.setOnAction(e -> tableView.setMinWidth(tableView.getMinWidth()
- 10));
        Button button3 = new Button("Reset");
        button3.setOnAction(e -> tableView.setMinWidth(500));
        Button button4 = new Button("Increase 1px");
        button4.setOnAction(e -> tableView.setMinWidth(tableView.getMinWidth()
+ 1));
        Button button5 = new Button("Increase 10px");
        button5.setOnAction(e -> tableView.setMinWidth(tableView.getMinWidth()
+ 10));

        HBox row = new HBox(button1, button2, button3, button4, button5);
        row.setSpacing(10);
        TextArea output = new TextArea();

        addWidthListeners(tableView, output);
        VBox root = new VBox(row, new Group(tableView), output);
        root.setSpacing(10);
        root.setPadding(new Insets(10));

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Constrained Resize Policy Issue TableView");
        primaryStage.show();
    }

    private void addWidthListeners(TableView<Person> tableView,
TextArea output)
{
        tableView.widthProperty().addListener((obs, old, val) -> {
            String str = "Table width changed :: " + val + "\n";
            output.setText(output.getText() + str);
            output.positionCaret(output.getText().length());
        });
        tableView.getColumns().forEach(column -> {
            column.widthProperty().addListener((obs, old, width) -> {
                String str = " ---> " + column.getText() + " : width
changed to : " + column.getWidth()+"\n";
                output.setText(output.getText() + str);
                output.positionCaret(output.getText().length());
            });
        });
    }

    class Person {
        private StringProperty firstName = new SimpleStringProperty();
        private StringProperty price = new SimpleStringProperty();
        private StringProperty total = new SimpleStringProperty();

        public Person(String fn, String price, String total) {
            setFirstName(fn);
            setPrice(price);
            setTotal(total);
        }

        public StringProperty firstNameProperty() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName.set(firstName);
        }

        public StringProperty priceProperty() {
            return price;
        }

        public void setPrice(String price) {
            this.price.set(price);
        }

        public StringProperty totalProperty() {
            return total;
        }

        public void setTotal(String total) {
            this.total.set(total);
        }
    }
}



*Fix:*

On investigating the code, it is noticed that there is an explicit line of
code to check if the difference in widths is greater than 1px. I think this
should be greater than 0px. Because we need to recompute the column widths
even if the width is increased by 1px.



The part of the code that need to be updated is in TableUtil.java ->
constrainedResize(..) method -> line 226



if (Math.*abs*(colWidth - tableWidth) > 1) {



If I update the value 1 to 0, the issue is fixed and the hBar visibility is
computed correctly.



I have submitted a pull request <https://github.com/openjdk/jfx/pull/848>
for this fix. This is the first time I am submitting the pull request, so I
am not sure about the exact process. However I do read the guidelines, and
created a test case that fails with the current code and passes after
including this change.  My OCA is approved and waiting for the bot to pick
it.



Thanks & Regards,
Sai Pradeep Dandem
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20220725/d6f666ec/attachment-0001.htm>


More information about the openjfx-dev mailing list