Created
January 13, 2023 14:08
-
-
Save maroc81/6c60d5ae11fdf79099e50fe308509a27 to your computer and use it in GitHub Desktop.
JavaFX TextAreaTableCell in kotlin
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
package atpmaker.ui.common | |
import com.sun.javafx.scene.control.LabeledText | |
import javafx.beans.binding.Bindings | |
import javafx.beans.property.ObjectProperty | |
import javafx.beans.property.SimpleObjectProperty | |
import javafx.event.EventHandler | |
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.scene.input.KeyEvent | |
import javafx.scene.text.Text | |
import javafx.util.Callback | |
import javafx.util.StringConverter | |
import javafx.util.converter.DefaultStringConverter | |
/** | |
* Class for editing a table cell in a text area control | |
* | |
* Adapted from https://gist.github.com/eckig/30abf0d7d51b7756c2e7?permalink_comment_id=2395960 | |
*/ | |
class TextAreaTableCell<S, T>(converter: StringConverter<T>? = null) : | |
TableCell<S, T>() { | |
/** | |
* The text area for editing the table cell, created only when the | |
* cell is edited so an object isn't created until needed | |
*/ | |
private val textArea: TextArea by lazy { | |
createTextArea(this, getConverter()) | |
} | |
/** The text node to display the text when not editing */ | |
private val cellText: Text by lazy { createText() } | |
/** | |
* The converter used to produce string for the table cell contents | |
*/ | |
private val converter: ObjectProperty<StringConverter<T>?> = SimpleObjectProperty(this, "converter") | |
init { | |
styleClass.add("text-area-table-cell") | |
setConverter(converter) | |
} | |
fun converterProperty(): ObjectProperty<StringConverter<T>?> { | |
return converter | |
} | |
fun setConverter(value: StringConverter<T>?) { | |
converterProperty().set(value) | |
} | |
fun getConverter(): StringConverter<T>? { | |
return converterProperty().get() | |
} | |
override fun startEdit() { | |
if (!isEditable || !tableView.isEditable || !tableColumn.isEditable) { | |
return | |
} | |
super.startEdit() | |
if (isEditing) { | |
textArea.text = getItemText<T>(this, getConverter()) | |
text = null | |
graphic = textArea | |
textArea.selectAll() | |
textArea.requestFocus() | |
} | |
} | |
override fun cancelEdit() { | |
super.cancelEdit() | |
text = null | |
graphic = cellText | |
} | |
public override fun updateItem(item: T, empty: Boolean) { | |
super.updateItem(item, empty) | |
if (isEmpty) { | |
text = null | |
graphic = null | |
} else { | |
if (isEditing) { | |
textArea.text = getItemText<T>(this, getConverter()) | |
text = null | |
graphic = textArea | |
} else { | |
//text = getItemText<T>(this, getConverter()) | |
text = null | |
graphic = cellText | |
} | |
} | |
} | |
private fun <T> getItemText(cell: Cell<T>, converter: StringConverter<T>?): String { | |
return converter?.toString(cell.item) ?: cell.item?.toString() ?: "" | |
} | |
/** | |
* Creates the text node used to display the text when not editing | |
*/ | |
private fun createText(): Text { | |
val text = LabeledText(this) | |
// Bind the width to wrap the text in the cell to the width of the | |
// cell minus the text gap width | |
text.wrappingWidthProperty() | |
.bind(this.widthProperty() | |
.subtract(Bindings.multiply(2.0, graphicTextGapProperty()))) | |
// Bind the text property to the item property converted to a string | |
text.textProperty().bind(Bindings.createStringBinding({ | |
getItemText<T>(this, getConverter()) | |
}, | |
itemProperty() | |
)) | |
return text | |
} | |
/** | |
* Creates the text area control used by the cell for editing | |
*/ | |
private fun <T> createTextArea(cell: Cell<T>, converter: StringConverter<T>?): TextArea { | |
val textArea = TextArea(getItemText<T>(cell, converter)) | |
textArea.onKeyReleased = EventHandler { t: KeyEvent -> | |
if (t.code == KeyCode.ESCAPE) { | |
cell.cancelEdit() | |
t.consume() | |
} else if (t.code == KeyCode.ENTER && t.isShiftDown) { | |
checkNotNull(converter) { | |
("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.text)) | |
t.consume() | |
} | |
} | |
textArea.prefRowCountProperty().bind(Bindings.size(textArea.paragraphs).add(1)) | |
return textArea | |
} | |
companion object { | |
/** | |
* Creates a text area table cell for string columns | |
*/ | |
fun <S> forTableColumn(): Callback<TableColumn<S, String>, TableCell<S, String>> { | |
return forTableColumn(DefaultStringConverter()) | |
} | |
/** | |
* Creates a text area table cell for a column of type [T] and the given [converter] | |
*/ | |
fun <S, T> forTableColumn(converter: StringConverter<T>): Callback<TableColumn<S, T>, TableCell<S, T>> { | |
return Callback { column: TableColumn<S, T>? -> | |
TextAreaTableCell<S, T>(converter) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment