Skip to content

Instantly share code, notes, and snippets.

@maroc81
Created January 13, 2023 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maroc81/6c60d5ae11fdf79099e50fe308509a27 to your computer and use it in GitHub Desktop.
Save maroc81/6c60d5ae11fdf79099e50fe308509a27 to your computer and use it in GitHub Desktop.
JavaFX TextAreaTableCell in kotlin
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