-
Bug
-
Resolution: Unresolved
-
P3
-
jfx16
To guarantee that all event handlers/filters see the same event state, it must be effectively unmodifiable. CellEditEvents aren't, failing test:
@Test
public void testTableCellEditEventUnmodifiable() {
TableView<MenuItem> table = createEditableTable();
TableColumn<MenuItem, String> editingColumn =
(TableColumn<MenuItem, String>) table.getColumns().get(0);
int editingRow = 1;
String oldValue = table.getItems().get(editingRow).getText();
String newValue = "edited";
editingColumn.addEventHandler(TableColumn.<MenuItem, String>editCommitEvent(), e -> {
// do commit
table.getItems().get(editingRow).setText(newValue);
assertEquals(oldValue, e.getOldValue());
});
TablePosition<MenuItem, String> editingPosition = new TablePosition<>(table, editingRow, editingColumn);
CellEditEvent event = new CellEditEvent(table, editingPosition, TableColumn.editCommitEvent(), newValue);
Event.fireEvent(editingColumn, event);
}
Reason is that getOldValue (and also getRowValue) is implemented as a "live" lookup into current table data.
Fix would be to follow the lead of editEvent in List/Tree-EditEvent and keep references to the state at instantiation of the event
#########################################################################################################
Another example:
The TextField will commit as soon as you lose the focus.
This works for every possible use case but not if you click on the indentation node while editing the second TreeItem (TreeItem "B").
This is due the row value is done via a 'live lookup' as described above, which won't work anymore as the row is collapsed via the indentation node.
// EXAMPLE START
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellEditEvent;
import javafx.scene.control.TreeTablePosition;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
TreeTableView<String> table = new TreeTableView<>();
TreeTableColumn<String, String> col = new TreeTableColumn<>("HI");
col.setCellFactory(e -> new TreeTableCell<>() {
@Override
public void startEdit() {
super.startEdit();
TextField value = new TextField(getText());
value.focusedProperty().addListener((inv, oldV, newV) -> {
if (!newV) {
TreeTableView<String> table = getTreeTableView();
// Inform the TableView of the edit being ready to be committed.
final TreeTablePosition<String, String> tablePosition = new TreeTablePosition<>(table,
getTableRow().getIndex(), getTableColumn());
CellEditEvent<String, String> cellEditEvent = new CellEditEvent<>(table, tablePosition,
TreeTableColumn.editCommitEvent(), value.getText());
Event.fireEvent(getTableColumn(), cellEditEvent);
// Update cell.
updateItem(value.getText(), false);
}
});
setGraphic(value);
value.requestFocus();
}
@Override
public void commitEdit(String newValue) {
System.out.println(isEditing());
startEdit();
super.commitEdit(newValue);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(null);
if (empty) {
setText(null);
} else {
setText(item);
}
}
});
col.setCellValueFactory(e -> new SimpleStringProperty(e.getValue().getValue()));
col.setOnEditCommit(e -> {
System.out.println("COMMIT");
TreeItem<String> rowValue = e.getRowValue();
// NPE when 'comitting via indentation node'.
rowValue.setValue(e.getNewValue());
});
table.getColumns().add(col);
table.setEditable(true);
TreeItem<String> item = new TreeItem<>("A");
item.getChildren().add(new TreeItem<>("B"));
table.setRoot(item);
Pane root = new Pane();
root.getChildren().add(table);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
// EXAMPLE END
@Test
public void testTableCellEditEventUnmodifiable() {
TableView<MenuItem> table = createEditableTable();
TableColumn<MenuItem, String> editingColumn =
(TableColumn<MenuItem, String>) table.getColumns().get(0);
int editingRow = 1;
String oldValue = table.getItems().get(editingRow).getText();
String newValue = "edited";
editingColumn.addEventHandler(TableColumn.<MenuItem, String>editCommitEvent(), e -> {
// do commit
table.getItems().get(editingRow).setText(newValue);
assertEquals(oldValue, e.getOldValue());
});
TablePosition<MenuItem, String> editingPosition = new TablePosition<>(table, editingRow, editingColumn);
CellEditEvent event = new CellEditEvent(table, editingPosition, TableColumn.editCommitEvent(), newValue);
Event.fireEvent(editingColumn, event);
}
Reason is that getOldValue (and also getRowValue) is implemented as a "live" lookup into current table data.
Fix would be to follow the lead of editEvent in List/Tree-EditEvent and keep references to the state at instantiation of the event
#########################################################################################################
Another example:
The TextField will commit as soon as you lose the focus.
This works for every possible use case but not if you click on the indentation node while editing the second TreeItem (TreeItem "B").
This is due the row value is done via a 'live lookup' as described above, which won't work anymore as the row is collapsed via the indentation node.
// EXAMPLE START
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellEditEvent;
import javafx.scene.control.TreeTablePosition;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
TreeTableView<String> table = new TreeTableView<>();
TreeTableColumn<String, String> col = new TreeTableColumn<>("HI");
col.setCellFactory(e -> new TreeTableCell<>() {
@Override
public void startEdit() {
super.startEdit();
TextField value = new TextField(getText());
value.focusedProperty().addListener((inv, oldV, newV) -> {
if (!newV) {
TreeTableView<String> table = getTreeTableView();
// Inform the TableView of the edit being ready to be committed.
final TreeTablePosition<String, String> tablePosition = new TreeTablePosition<>(table,
getTableRow().getIndex(), getTableColumn());
CellEditEvent<String, String> cellEditEvent = new CellEditEvent<>(table, tablePosition,
TreeTableColumn.editCommitEvent(), value.getText());
Event.fireEvent(getTableColumn(), cellEditEvent);
// Update cell.
updateItem(value.getText(), false);
}
});
setGraphic(value);
value.requestFocus();
}
@Override
public void commitEdit(String newValue) {
System.out.println(isEditing());
startEdit();
super.commitEdit(newValue);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(null);
if (empty) {
setText(null);
} else {
setText(item);
}
}
});
col.setCellValueFactory(e -> new SimpleStringProperty(e.getValue().getValue()));
col.setOnEditCommit(e -> {
System.out.println("COMMIT");
TreeItem<String> rowValue = e.getRowValue();
// NPE when 'comitting via indentation node'.
rowValue.setValue(e.getNewValue());
});
table.getColumns().add(col);
table.setEditable(true);
TreeItem<String> item = new TreeItem<>("A");
item.getChildren().add(new TreeItem<>("B"));
table.setRoot(item);
Pane root = new Pane();
root.getChildren().add(table);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
// EXAMPLE END