-
-
Save TurekBot/69495ba2b15771c8118f4be1ec722381 to your computer and use it in GitHub Desktop.
An attempt to improve the already great TextAreaTableCell, see Stack Overflow question: http://stackoverflow.com/questions/27666382/editable-multiline-cell-in-tableview
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
/** | |
* Creates a TableCell that contains a TextArea similar to a TextFieldTableCell. | |
* <p> | |
* Originally created by eckig: https://gist.github.com/eckig/30abf0d7d51b7756c2e7 | |
* <p> | |
* Adapted from the original TextAreaTableCell by Brad Turek. | |
* @param <S> | |
* @param <T> | |
*/ | |
@SuppressWarnings("WeakerAccess") | |
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.setWrapText(true); | |
textArea.setOnKeyPressed(t -> { | |
if (t.getCode() == KeyCode.ESCAPE) { | |
cell.cancelEdit(); | |
t.consume(); | |
} | |
else if(t.getCode() == KeyCode.ENTER && t.isShiftDown()) { | |
t.consume(); | |
textArea.insertText(textArea.getCaretPosition(), "\n"); | |
} | |
else if(t.getCode() == KeyCode.ENTER) { | |
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(); | |
} | |
}); | |
return textArea; | |
} | |
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(); | |
} | |
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); | |
cell.setTooltip(null); | |
} else { | |
if (cell.isEditing()) { | |
if (textArea != null) { | |
textArea.setText(getItemText(cell, converter)); | |
} | |
cell.setText(null); | |
cell.setGraphic(textArea); | |
cell.setTooltip(null); | |
} else { | |
cell.setText(getItemText(cell, converter)); | |
cell.setGraphic(null); | |
//Add text as tooltip so that user can read text without editing it. | |
Tooltip tooltip = new Tooltip(getItemText(cell, converter)); | |
tooltip.setWrapText(true); | |
tooltip.prefWidthProperty().bind(cell.widthProperty()); | |
cell.setTooltip(tooltip); | |
} | |
} | |
} | |
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()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You should add a null check here: