-
-
Save eckig/30abf0d7d51b7756c2e7 to your computer and use it in GitHub Desktop.
import javafx.beans.binding.Bindings; | |
import javafx.beans.property.ObjectProperty; | |
import javafx.beans.property.SimpleObjectProperty; | |
import javafx.scene.control.Cell; | |
import javafx.scene.control.TableCell; | |
import javafx.scene.control.TableColumn; | |
import javafx.scene.control.TextArea; | |
import javafx.scene.input.KeyCode; | |
import javafx.util.Callback; | |
import javafx.util.StringConverter; | |
import javafx.util.converter.DefaultStringConverter; | |
public class TextAreaTableCell<S, T> extends TableCell<S, T> { | |
public static <S> Callback<TableColumn<S, String>, TableCell<S, String>> forTableColumn() { | |
return forTableColumn(new DefaultStringConverter()); | |
} | |
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(final StringConverter<T> converter) { | |
return list -> new TextAreaTableCell<>(converter); | |
} | |
private static <T> String getItemText(Cell<T> cell, StringConverter<T> converter) { | |
return converter == null ? cell.getItem() == null ? "" : cell.getItem() | |
.toString() : converter.toString(cell.getItem()); | |
} | |
private static <T> TextArea createTextArea(final Cell<T> cell, final StringConverter<T> converter) { | |
TextArea textArea = new TextArea(getItemText(cell, converter)); | |
textArea.setOnKeyReleased(t -> { | |
if (t.getCode() == KeyCode.ESCAPE) { | |
cell.cancelEdit(); | |
t.consume(); | |
} | |
else if(t.getCode() == KeyCode.ENTER && t.isShiftDown()) { | |
if (converter == null) { | |
throw new IllegalStateException( | |
"Attempting to convert text input into Object, but provided " | |
+ "StringConverter is null. Be sure to set a StringConverter " | |
+ "in your cell factory."); | |
} | |
cell.commitEdit(converter.fromString(textArea.getText())); | |
t.consume(); | |
} | |
}); | |
textArea.prefRowCountProperty().bind(Bindings.size(textArea.getParagraphs())); | |
return textArea; | |
} | |
private void startEdit(final Cell<T> cell, final StringConverter<T> converter) { | |
textArea.setText(getItemText(cell, converter)); | |
cell.setText(null); | |
cell.setGraphic(textArea); | |
textArea.selectAll(); | |
textArea.requestFocus(); | |
} | |
private static <T> void cancelEdit(Cell<T> cell, final StringConverter<T> converter) { | |
cell.setText(getItemText(cell, converter)); | |
cell.setGraphic(null); | |
} | |
private void updateItem(final Cell<T> cell, final StringConverter<T> converter) { | |
if (cell.isEmpty()) { | |
cell.setText(null); | |
cell.setGraphic(null); | |
} else { | |
if (cell.isEditing()) { | |
if (textArea != null) { | |
textArea.setText(getItemText(cell, converter)); | |
} | |
cell.setText(null); | |
cell.setGraphic(textArea); | |
} else { | |
cell.setText(getItemText(cell, converter)); | |
cell.setGraphic(null); | |
} | |
} | |
} | |
private TextArea textArea; | |
private ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>(this, "converter"); | |
public TextAreaTableCell() { | |
this(null); | |
} | |
public TextAreaTableCell(StringConverter<T> converter) { | |
this.getStyleClass().add("text-area-table-cell"); | |
setConverter(converter); | |
} | |
public final ObjectProperty<StringConverter<T>> converterProperty() { | |
return converter; | |
} | |
public final void setConverter(StringConverter<T> value) { | |
converterProperty().set(value); | |
} | |
public final StringConverter<T> getConverter() { | |
return converterProperty().get(); | |
} | |
@Override | |
public void startEdit() { | |
if (!isEditable() || !getTableView().isEditable() || !getTableColumn().isEditable()) { | |
return; | |
} | |
super.startEdit(); | |
if (isEditing()) { | |
if (textArea == null) { | |
textArea = createTextArea(this, getConverter()); | |
} | |
startEdit(this, getConverter()); | |
} | |
} | |
@Override | |
public void cancelEdit() { | |
super.cancelEdit(); | |
cancelEdit(this, getConverter()); | |
} | |
@Override | |
public void updateItem(T item, boolean empty) { | |
super.updateItem(item, empty); | |
updateItem(this, getConverter()); | |
} | |
} |
Should not be textArea.appendText("\n")
, but rather textArea.insertText(textArea.getCaretPosition(), "\n")
. With your code, when the caret is within the text, "Enter" adds a new line at the end. It took me a long time to figure this out.
I found that in the createTextArea
method using textArea.setOnKeyReleased(...
let an extra newline get in there before commiting. Using textArea.setOnKeyPressed(...
instead fixed that for me. (Also, thanks @limitedAtonement and @DominikStyp for the tips.)
I'm new to Gists—if you can edit them, I can't figure out how—but I do have a fork with the above-mentioned improvements.
An improvement to the text area sizing:
private void startEdit(final Cell<T> cell, final StringConverter<T> converter) {
textArea.setText(getItemText(cell, converter));
cell.setText(null);
cell.setGraphic(textArea);
// Make sure the text area stays the right size:
/* The following solution is courtesy of James_D: https://stackoverflow.com/a/22733264/5432315 */
// Perform a lookup for an element with a css class of "text"
// This will give the Node that actually renders the text inside the
// TextArea
Node text = textArea.lookup(".text");
// Bind the preferred height of the text area to the actual height of the text
// This will make the text area the height of the text, plus some padding
// of 20 pixels, as long as that height is between the text area's minHeight
// and maxHeight. The max height will be the height of its parent (usually).
textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() ->
text.getBoundsInLocal().getHeight(), text.boundsInLocalProperty()).add(20)
);
textArea.selectAll();
textArea.requestFocus();
}
Thanks for this! I adapted it for a kotlin implementation that also wraps the text in the cell.
https://gist.github.com/maroc81/6c60d5ae11fdf79099e50fe308509a27
Thanks very much for this implementation - shame that Oracle didn't do this...
I think that "chat version" of the key's would be better - so if you click ENTER you confirm the change, and if you click SHIFT + ENTER you go to new line: