3

I'm seeing a problem where the far-right column of a JavaFX TableView is being rendered incorrectly, overrunning the width of the TableView itself and in some circumstances almost entirely disappearing.

The behaviour is best described with a screenshot:

Sample code screenshot with ScenicView highlighting overrun

Here is some quick 'n dirty sample code which reproduces this issue:

package sample;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import java.util.stream.IntStream;

public class Main extends Application {

    private TableView<TestObject> table = new TableView<>();

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

    @Override
    public void start(Stage stage) {

        IntStream.range(1, 26).forEach(counter -> {
            TableColumn<TestObject, String> column = new TableColumn<>("Field " + counter);
            column.setCellValueFactory(new PropertyValueFactory<>("field" + counter));
            column.setCellFactory(col -> new TableCell<TestObject, String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);

                    if (item == null || empty)
                        setText(null);
                    else
                        setText(item);
                }
            });

            if (counter == 25)
                column.setStyle("-fx-alignment: CENTER_RIGHT;");

            table.getColumns().add(column);
        });

        table.getSelectionModel().setCellSelectionEnabled(true);
        table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
        table.setItems(FXCollections.observableArrayList(new TestObject()));

        BorderPane borderPane = new BorderPane(table);
        borderPane.setRight(new Label("Panel allowing ScenicView to\nhighlight full extent of the issue"));

        Scene scene = new Scene(borderPane);
        stage.setScene(scene);
        stage.setTitle("TableView Column Issue");
        stage.setWidth(800);
        stage.setHeight(300);
        stage.show();
    }

    public static class TestObject {

        private final SimpleStringProperty field1;
        private final SimpleStringProperty field2;
        private final SimpleStringProperty field3;
        private final SimpleStringProperty field4;
        private final SimpleStringProperty field5;
        private final SimpleStringProperty field6;
        private final SimpleStringProperty field7;
        private final SimpleStringProperty field8;
        private final SimpleStringProperty field9;
        private final SimpleStringProperty field10;
        private final SimpleStringProperty field11;
        private final SimpleStringProperty field12;
        private final SimpleStringProperty field13;
        private final SimpleStringProperty field14;
        private final SimpleStringProperty field15;
        private final SimpleStringProperty field16;
        private final SimpleStringProperty field17;
        private final SimpleStringProperty field18;
        private final SimpleStringProperty field19;
        private final SimpleStringProperty field20;
        private final SimpleStringProperty field21;
        private final SimpleStringProperty field22;
        private final SimpleStringProperty field23;
        private final SimpleStringProperty field24;
        private final SimpleStringProperty field25;

        private SimpleStringProperty newProp() {
            return new SimpleStringProperty("Scroll to the far right column");
        }

        private TestObject() {
            this.field1 = newProp();
            this.field2 = newProp();
            this.field3 = newProp();
            this.field4 = newProp();
            this.field5 = newProp();
            this.field6 = newProp();
            this.field7 = newProp();
            this.field8 = newProp();
            this.field9 = newProp();
            this.field10 = newProp();
            this.field11 = newProp();
            this.field12 = newProp();
            this.field13 = newProp();
            this.field14 = newProp();
            this.field15 = newProp();
            this.field16 = newProp();
            this.field17 = newProp();
            this.field18 = newProp();
            this.field19 = newProp();
            this.field20 = newProp();
            this.field21 = newProp();
            this.field22 = newProp();
            this.field23 = newProp();
            this.field24 = newProp();
            this.field25 = new SimpleStringProperty("Can you see down to zero? 9876543210");
        }

        public String getField1() { return field1.get(); }
        public void setField(String str) { field1.set(str); }

        public String getField2() { return field2.get(); }
        public void setField2(String str) { field2.set(str); }

        public String getField3() { return field3.get(); }
        public void setField3(String str) { field3.set(str); }

        public String getField4() { return field4.get(); }
        public void setField4(String str) { field4.set(str); }

        public String getField5() { return field5.get(); }
        public void setField5(String str) { field5.set(str); }

        public String getField6() { return field6.get(); }
        public void setField6(String str) { field6.set(str); }

        public String getField7() { return field7.get(); }
        public void setField7(String str) { field7.set(str); }

        public String getField8() { return field8.get(); }
        public void setField8(String str) { field8.set(str); }

        public String getField9() { return field9.get(); }
        public void setField9(String str) { field9.set(str); }

        public String getField10() { return field10.get(); }
        public void setField10(String str) { field10.set(str); }

        public String getField11() { return field11.get(); }
        public void setField11(String str) { field11.set(str); }

        public String getField12() { return field12.get(); }
        public void setField12(String str) { field12.set(str); }

        public String getField13() { return field13.get(); }
        public void setField13(String str) { field13.set(str); }

        public String getField14() { return field14.get(); }
        public void setField14(String str) { field14.set(str); }

        public String getField15() { return field15.get(); }
        public void setField15(String str) { field15.set(str); }

        public String getField16() { return field16.get(); }
        public void setField16(String str) { field16.set(str); }

        public String getField17() { return field17.get(); }
        public void setField17(String str) { field17.set(str); }

        public String getField18() { return field18.get(); }
        public void setField18(String str) { field18.set(str); }

        public String getField19() { return field19.get(); }
        public void setField19(String str) { field19.set(str); }

        public String getField20() { return field20.get(); }
        public void setField20(String str) { field20.set(str); }

        public String getField21() { return field21.get(); }
        public void setField21(String str) { field21.set(str); }

        public String getField22() { return field22.get(); }
        public void setField22(String str) { field22.set(str); }

        public String getField23() { return field23.get(); }
        public void setField23(String str) { field23.set(str); }

        public String getField24() { return field24.get(); }
        public void setField24(String str) { field24.set(str); }

        public String getField25() { return field25.get(); }
        public void setField25(String str) { field25.set(str); }
    }
}

Observations so far:

  1. If you look at the screenshot I've included, note how ScenicView is showing for the selected TableRow a boundsInParent width of 4283, but the layoutBounds width is a smaller 4259.75. The difference of 23.25 matches the amount it has overrun, if measuring the pixels using a graphics tool.
  2. The more columns there are added, the greater the issue becomes. In this sample code, 25 columns was enough to make it obvious. In the application I'm working on currently, we can have as many as 100 columns, which causes the far-right column to almost completely disappear.
  3. If there is no column.setCellFactory defined, and the table instead renders the raw contents, the issue goes away.
  4. The effect is universal to all platforms (MacOS, Windows and Linux), although seemingly more exaggerated on Windows.
  5. The issue exists across various JDK versions, including 1.8.0_172.

Has anyone please got any ideas on how to fix this, or failing that work around it in such a way that no matter how many columns there are, we never lose any part of the far-right column? That would be greatly appreciated!

Warm regards

Ed

EdK
  • 81
  • 5
  • https://stackoverflow.com/questions/37027298/set-constrained-resize-policy-for-columns-without-first-for-number-of-row-in-t – SedJ601 Apr 25 '18 at 03:57
  • faintly remember that this is a bug (no # at hand, sry) and fixed in fx11 ... which will no longer be part of java se ... – kleopatra Apr 25 '18 at 08:08
  • @Sedrick Thanks for trying to help, but I'm not sure the link you sent is related to the same problem - would it be possible for you to modify my sample code to demonstrate how it might be applied? – EdK Apr 25 '18 at 08:42
  • @kleopatra Thanks for this info, it's comforting to know both that others have experienced this problem, and that in FX11 it has been identified and fixed already. I'd really like to track down the bug #, since it might give me some direction on an interim workaround - any chance you recall any clues from the depths of your memory that might help with that? :) – EdK Apr 25 '18 at 08:50
  • The issue I had in the far back of my mind is https://bugs.openjdk.java.net/browse/JDK-8192800 - but now really reading your question (cough ;), it looks like a different problem, more like rounding error somewhere in the header ... sry, no fun .. – kleopatra Apr 25 '18 at 09:07
  • well ... now tested -- and seeing the zero in fx9 :) – kleopatra Apr 25 '18 at 09:23
  • @kleopatra In the process of trying that myself, fingers crossed. Thanks so much for taking the time to look further into this - legend :) – EdK Apr 25 '18 at 10:04

1 Answers1

0

I can confirm now that, as suggested by kleopatra, using the latest JDK (at time of writing 10.0.1) solves this problem :)

EdK
  • 81
  • 5