Happen if we call table.edit(row,... ) on a newly added item off the visible range. Looks like internals get confused.
To reproduce, run the example below
* - click addAndEdit to insert cell off the visual range and start edit
* - click scrollBar to scroll to inserted cell
* - click addAndEdit again
* - expected: cell with "initial 51" is edited
* - actual: cell with "initial 50" is edited
* - type x at beginning of editor text field, commit by enter
* - note two cells with "xinitial 50"
* - scroll back and forth to get rid of the visual artefact (only one is really changed)
* - note that "initial 51" is changed to "xinitial 50" - corrupted data!
The example:
public class TablePCoreAddAndEdit extends Application {
private Parent getContent() {
TableView<Dummy> table = new TableView<>(createData(50));
table.setEditable(true);
TableColumn<Dummy, String> column = new TableColumn<>("Value");
column.setCellValueFactory(c -> c.getValue().valueProperty());
column.setCellFactory(TextFieldTableCell.forTableColumn());
column.setMinWidth(200);
table.getColumns().addAll(column);
// insert and start editing at an invisible row
// in my environment, I see about 12 rows
int insertIndex = 20;
Button addAndEdit = new Button("AddAndEdit");
addAndEdit.setOnAction(e -> {
Dummy dummy = new Dummy();
table.getItems().add(insertIndex, dummy);
table.edit(insertIndex, column);
LOG.info("insertIndex" + insertIndex + "isAtIndex " + table.getItems().indexOf(dummy) + dummy);
});
Button logEditing = new Button("LogEditing");
logEditing.setOnAction(e-> {
TablePosition<?, ?> editingCell = table.getEditingCell();
LOG.info((editingCell != null
? "editing row: " + editingCell.getRow() + table.getItems().get(editingCell.getRow()): "no editing cell")
+ "value at insertIndex: " + table.getItems().get(insertIndex)
);
});
HBox buttons = new HBox(10, addAndEdit, logEditing);
BorderPane content = new BorderPane(table);
content.setBottom(buttons);
return content;
}
private ObservableList<Dummy> createData(int size) {
return FXCollections.observableArrayList(
Stream.generate(Dummy::new)
.limit(size)
.collect(Collectors.toList()));
}
private static class Dummy {
private static int count;
StringProperty value = new SimpleStringProperty(this, "value", "initial " + count++);
public StringProperty valueProperty() {return value;}
public String getValue() {return valueProperty().get(); }
public void setValue(String text) {valueProperty().set(text); }
public String toString() {return "[dummy: " + getValue() + "]";}
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
//primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TablePCoreAddAndEdit.class.getName());
}
To reproduce, run the example below
* - click addAndEdit to insert cell off the visual range and start edit
* - click scrollBar to scroll to inserted cell
* - click addAndEdit again
* - expected: cell with "initial 51" is edited
* - actual: cell with "initial 50" is edited
* - type x at beginning of editor text field, commit by enter
* - note two cells with "xinitial 50"
* - scroll back and forth to get rid of the visual artefact (only one is really changed)
* - note that "initial 51" is changed to "xinitial 50" - corrupted data!
The example:
public class TablePCoreAddAndEdit extends Application {
private Parent getContent() {
TableView<Dummy> table = new TableView<>(createData(50));
table.setEditable(true);
TableColumn<Dummy, String> column = new TableColumn<>("Value");
column.setCellValueFactory(c -> c.getValue().valueProperty());
column.setCellFactory(TextFieldTableCell.forTableColumn());
column.setMinWidth(200);
table.getColumns().addAll(column);
// insert and start editing at an invisible row
// in my environment, I see about 12 rows
int insertIndex = 20;
Button addAndEdit = new Button("AddAndEdit");
addAndEdit.setOnAction(e -> {
Dummy dummy = new Dummy();
table.getItems().add(insertIndex, dummy);
table.edit(insertIndex, column);
LOG.info("insertIndex" + insertIndex + "isAtIndex " + table.getItems().indexOf(dummy) + dummy);
});
Button logEditing = new Button("LogEditing");
logEditing.setOnAction(e-> {
TablePosition<?, ?> editingCell = table.getEditingCell();
LOG.info((editingCell != null
? "editing row: " + editingCell.getRow() + table.getItems().get(editingCell.getRow()): "no editing cell")
+ "value at insertIndex: " + table.getItems().get(insertIndex)
);
});
HBox buttons = new HBox(10, addAndEdit, logEditing);
BorderPane content = new BorderPane(table);
content.setBottom(buttons);
return content;
}
private ObservableList<Dummy> createData(int size) {
return FXCollections.observableArrayList(
Stream.generate(Dummy::new)
.limit(size)
.collect(Collectors.toList()));
}
private static class Dummy {
private static int count;
StringProperty value = new SimpleStringProperty(this, "value", "initial " + count++);
public StringProperty valueProperty() {return value;}
public String getValue() {return valueProperty().get(); }
public void setValue(String text) {valueProperty().set(text); }
public String toString() {return "[dummy: " + getValue() + "]";}
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
//primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TablePCoreAddAndEdit.class.getName());
}
- relates to
-
JDK-8264127 ListCell editing status is true, when index changes while editing
- Resolved
-
JDK-8265206 Tree-/TableCell: editing state not updated on cell re-use
- Resolved
-
JDK-8265210 TreeCell: cell editing state not updated on cell re-use
- Resolved
-
JDK-8171847 More than one table cell are in edit state
- Open
-
JDK-8272118 ListViewSkin et al: must not cancel edit on scrolling
- Resolved