JavaFX tab through editable cells - javafx

I have defined my own editable cell for a TableView. Everything works great but I want to be able to tab through the cells. Say I'm in cell [2,2] on a tab press I want the selected cell to go to [3,2]. Is this possible? How should I go about achieving this? Should I add a listener to the TableCell or the TableColumn?
Here is my custom TableCell
class EditingCell extends TableCell<Student, String> {
private TextField textField;
public EditingCell(Assignment assign) {
this.setTooltip(new Tooltip(assign.getMaxPoints() + " pts max"));
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}

You can achieve this by adding a KeyListener to your textField. Try adding the following code to your createTextField()
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(Integer.parseInt(textField.getText()));
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
} else if (t.getCode() == KeyCode.TAB) {
commitEdit(Integer.parseInt(textField.getText()));
TableColumn nextColumn = getNextColumn(!t.isShiftDown());
if (nextColumn != null) {
getTableView().edit(getTableRow().getIndex(), nextColumn);
}
}
}
});
There are a few more things that you will have to take care of. For a complete example, please follow
https://gist.github.com/abhinayagarwal/9383881

Related

Javafx: Table Column commit on Button click

I have method that make a TableColumn editable that I made according to this tutorial, with the two changes recommended in this question witch are changing focusedProperty with setOnAction and add binding. The method is like this:
public static <T> void setAutoCompleteTableColumn(TableColumn<T,String> column, BiConsumer<T, String> field, String type, BiConsumer handleUpdates, List autoComplete){
column.setCellFactory(param -> {
return new TableCell<T, String>(){
private TextField textField;
#Override
public void startEdit() {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField(){
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.setOnAction(event ->{
if(textField.getText() != ""){
commitEdit(textField.getText());
}
});
AutoCompletionBinding<T> binding = TextFields.bindAutoCompletion(textField,autoComplete);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
});
column.setOnEditCommit(event -> {
T model = event.getRowValue();
if (!event.getNewValue().isEmpty()) {
Cloner cloner = new Cloner();
final T oldModel = cloner.deepClone(model);
//Update the old value with the new value
field.accept(model, event.getNewValue());
//Retrieve changes
handleUpdates.accept(oldModel, model);
}
});
}
I use this method in the controller like this:
TableColumn<Purchase, String> nameColumn = new TableColumn<>();
setAutoCompleteTableColumn(nameColumn, (p,s) -> validatePurchaseEdit(p,s), "text", (o, n) -> updateTracker(o, n), appState.items);
I have a button click event that switch the TableView editable to false.
For some reason when this event is executed the current cell that I am editing does commit edit but an other cell(a random one) switches to edit mode. Can some one explain what is happening, and how can I make all cells commit edit?

How to commit changes on a JavaFX ListCell without enter key?

I want to commit changes on ListCell without enter key.
I have the following code :
objetListview1.setEditable(true);
objetListview1.setItems(FXCollections.observableArrayList(observableSet2));
objetListview1.setEditable(true);
objetListview1.setCellFactory(lv -> {
TextFieldListCell<Typ> cell = new TextFieldListCell<Typ>();
StringConverter<Typ> converter = new StringConverter<Typ>() {
#Override
public String toString(Typ obj) {
return obj.getTyp();
}
#Override
public Typ fromString(String string) {
Typ obj = cell.getItem();
if (obj == null) {
Typ newObj = new Typ("");
newObj.setTyp(string);
return newObj;
} else {
obj.setTyp(string);
return obj;
}
}
};
cell.setConverter(converter);
return cell;
});
Actually with this code, I can commit change with enter key.
Any help please?
I found the solution for my problem.
here is my code:
objetListview1.setCellFactory(lv -> new ListCell<Typ>() {
private TextField textField = new TextField() ;
{
textField.setOnAction(e -> {
commitEdit(getItem());
});
textField.focusedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
if (!newValue) {
System.out.println("Commiting " + textField.getText());
commitEdit(getItem());
}
});
}
#Override
protected void updateItem(Typ person, boolean empty) {
super.updateItem(person, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if (isEditing()) {
textField.setText(person.getTyp());
setText(null);
setGraphic(textField);
} else {
setText(person.getTyp());
setGraphic(null);
}
}
#Override
public void startEdit() {
super.startEdit();
textField.setText(getItem().getTyp());
setText(null);
setGraphic(textField);
textField.selectAll();
textField.requestFocus();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem().getTyp());
setGraphic(null);
}
#Override
public void commitEdit(Typ person) {
super.commitEdit(person);
person.setTyp(textField.getText());
setText(textField.getText());
setGraphic(null);
}
});

JavaFX: Different types of alignment in one column of TableView

Is it possible to do different types of alignment for each cell in one column of TableView?
E.g. I have a column with 3 possible types of data inside: positive number, negative number and "-". First should be aligned left, second - right and 3rd - center.
This is what I've tried:
private void setPositionAndColor(TableColumn<TransferItem, String> tableColumn){
tableColumn.setCellFactory(new Callback<TableColumn<TransferItem, String>, TableCell<TransferItem, String>>() {
#Override
public TableCell<TransferItem, String> call(TableColumn<TransferItem, String> column) {
TableCell<TransferItem, String> cell = new TableCell<TransferItem, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
setText(item);
if (item.matches("-+\\d.*")) {
setTextFill(Color.RED);
setTextAlignment(TextAlignment.RIGHT);
}
else if (item.equals("-")) {
setTextFill(Color.AQUA);
setTextAlignment(TextAlignment.CENTER);
}else {
setTextFill(Color.GREEN);
setTextAlignment(TextAlignment.LEFT);
}
}
}
};
return cell;
}
});
}
I added color changes just for testing and it works, but alignment doesn't.
Another approach:
Pos position;
private void setPositionAndColor(TableColumn<TransferItem, String> tableColumn){
tableColumn.setCellFactory(new Callback<TableColumn<TransferItem, String>, TableCell<TransferItem, String>>() {
#Override
public TableCell<TransferItem, String> call(TableColumn<TransferItem, String> column) {
TableCell<TransferItem, String> cell = new TableCell<TransferItem, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText("");
} else {
setText(item);
if (item.matches("-+\\d.*")) {
setTextFill(Color.RED);
position = Pos.CENTER_RIGHT;
}
else if (item.equals("-")) {
setTextFill(Color.AQUA);
position = Pos.CENTER;
}else {
setTextFill(Color.GREEN);
position = Pos.CENTER_LEFT;
}
}
}
};
cell.setAlignment(position);
return cell;
}
});
}
Doesn't work either.
Ty for help.
Ok, I got a working example - It seems, setAlignment() does the trick:
#Override
public void start(Stage primaryStage) {
ObservableList<String> data = FXCollections.observableArrayList();
IntStream.range(0, 1000).mapToObj(Integer::toString).forEach(data::add);
TableView<String> table = new TableView<String>(data);
TableColumn<String, String> column = new TableColumn<String, String>("test");
column.setCellFactory(c -> new TableCell<String,String>(){
#Override
protected void updateItem(String item, boolean empty) {
if(empty) {
setText(null);
}
else {
setText(item);
if(getIndex() % 2 == 0) {
setAlignment(Pos.CENTER_LEFT);
}
else if(getIndex() % 3 == 0) {
setAlignment(Pos.CENTER_RIGHT);
}
else {
setAlignment(Pos.CENTER);
}
}
}
});
column.setCellValueFactory(c -> new SimpleStringProperty(c.getValue()));
table.getColumns().add(column);
Scene scene = new Scene(new BorderPane(table), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}

JavaFX editable ComboBox in a table view

I am using a editable ComboBox cell in a table view. Here is my ComboBox cell class
public class ComboBoxCell extends TableCell<ClassesProperty, String> {
private ComboBox<String> comboBox;
public ComboBoxCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (comboBox == null) {
createComboBox();
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
comboBox.requestFocus();
comboBox.getEditor().requestFocus();
comboBox.getEditor().selectAll();
}
});
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (comboBox != null) {
comboBox.setValue(getString());
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createComboBox() {
// ClassesController.getLevelChoice() is the observable list of String
comboBox = new ComboBox<>(ClassesController.getLevelChoice());
comboBox.setEditable(true);
comboBox.setMinWidth(this.getWidth() - this.getGraphicTextGap()*2);
comboBox.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(comboBox.getSelectionModel().getSelectedItem());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
The problem I need to press three clicks on the table cell to get the text field of the combo box to edit the contents. Is there a way to make it in two clicks? I even used Platforn runlater but when I try to edit a cell at first time it takes three mouse clicks but at the second time only two clicks.
In your overridden cell.startEdit() method, Add a ListView with a textfield, then add this listview to setGraphic. This will show the listview directly once the row is selected and the cell is clicked, the listview will be inside the tablecell, I am yet to find a way to make it as a popup

Make javafx TableView act like JTable

Is there a way to make TableView in javafx to act just like JTable in swing?
The current procedure in TableView edit a cell content is:
Select the cell.
Pressing Enter on the Cell to get in Edit mode.
Typing.
Pressing Enter to commit edit.
while in jtable is much easier; you don't have to press enter to edit; you can edit directly.
Any ideas to let javafx table view act like jtable in this point?
I do it with a bunch of listeners. I'll add snips of my code since the columns won't really apply to you.
public class DataTable extends TableView<LineItem> {
private final Data data;
//used for keyreleased in table to put table in editing state and remember which key was pressed
private String lastKey = null;
/** makes a table
* #param data The Data structure
*/
public DataTable(Data data){
super(data.lines);
this.data = data;
setEditable(true);
//cut out my deleted lines stack but left some code because has to handle del in textfield
Callback<TableColumn<LineItem,String>, TableCell<LineItem,String>> txtCellFactory =
(TableColumn<LineItem,String> p) -> {return new EditingCell();};
TableColumn<LineItem,String> lineNoCol = new TableColumn<>("Line");
lineNoCol.setCellValueFactory(new PropertyValueFactory<>("LineNo"));
lineNoCol.setCellFactory(txtCellFactory);
lineNoCol.setOnEditCommit((TableColumn.CellEditEvent<LineItem, String> evt) -> {
evt.getTableView().getItems().get(evt.getTablePosition().getRow())
.setLineNo(evt.getNewValue());
});
//more columns
///// This puts it in editing state and remebers the key pressed for the textfield
setOnKeyPressed((KeyEvent t) -> {
TablePosition tp;
if (!t.isControlDown() &&
(t.getCode().isLetterKey() || t.getCode().isDigitKey())) {
lastKey = t.getText();
tp = getFocusModel().getFocusedCell();
edit(tp.getRow(),tp.getTableColumn());
lastKey = null;
}
});
addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent t) -> {
if (getEditingCell() == null && t.getCode() == KeyCode.ENTER) {
if (t.isShiftDown()) {
getSelectionModel().selectAboveCell();
} else {
getSelectionModel().selectBelowCell();
}
t.consume();
}
if (t.isControlDown() && t.getCode() == KeyCode.TAB) {
if (t.isShiftDown()) {
getSelectionModel().selectLeftCell();
} else {
getSelectionModel().selectRightCell();
}
t.consume();
}
});
setOnKeyReleased((KeyEvent t) -> {
TablePosition tp;
switch (t.getCode()) {
case INSERT:
data.lines.add(new LineItem());//maybe try adding at position
getSelectionModel().selectLast();
break;
case DELETE:
//textfield has to consume this so it doesn't reach here.
tp = getSelectionModel().getSelectedCells().get(0);
if (tp.getTableColumn() == lineNoCol) {
deletedLines.push(data.lines.remove(tp.getRow()));
//todo reselect something
} else { //maybe delete cell value
}
break;
case Z:
if (t.isControlDown()) {
if (!deletedLines.isEmpty()) {
data.lines.add(deletedLines.pop());
//todo re-sort etc
}
}
}
});
}
private class EditingCell extends TableCell {//no type for now
private TextField textField;
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
Platform.runLater(() -> {//without this space erases text, f2 doesn't
textField.requestFocus();//also selects
});
if (lastKey != null) {
textField.setText(lastKey);
Platform.runLater(() -> {
textField.deselect();
textField.end();
});
}
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
try {
setText(getItem().toString());
} catch (Exception e) {}
setGraphic(null);
}
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
//not applicable but shows how to align columns
if (getTableColumn().getText().equals("Amount"))
setAlignment(Pos.CENTER_RIGHT);
}
}
private void createTextField() {
textField = new TextField(getString());
//setPadding(Insets.EMPTY);//screws up table cell for after
textField.setPadding(new Insets(0,0,0,5));
textField.setPrefHeight(this.getHeight()-2);//border hgt??
textField.focusedProperty().addListener(
(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) -> {
if (!arg2) commitEdit(textField.getText());
});
textField.setOnKeyReleased((KeyEvent t) -> {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
}
else if (t.getCode() == KeyCode.RIGHT) {//dont really like this
if (textField.getCaretPosition() >= textField.getLength()){
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectRightCell();
}
}
else if (t.getCode() == KeyCode.UP) {//moves caret to 0
if (textField.getCaretPosition() == 0){
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectAboveCell();
}
}
else if (t.getCode() == KeyCode.DOWN) {
if (textField.getCaretPosition() >= textField.getLength()){
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
}
}
else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
textField.addEventFilter(KeyEvent.KEY_RELEASED, (KeyEvent t) -> {
if (t.getCode() == KeyCode.DELETE) {
t.consume();//stop from deleting line in table keyevent
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}

Resources