
tornadofx.Nodes.kt Maven / Gradle / Ivy
package tornadofx
import com.sun.javafx.scene.control.skin.TableColumnHeader
import javafx.beans.value.ObservableValue
import javafx.event.EventTarget
import javafx.scene.Node
import javafx.scene.control.*
import javafx.scene.input.InputEvent
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyEvent
import javafx.scene.input.MouseEvent
import javafx.scene.layout.GridPane
import javafx.scene.layout.Pane
import javafx.util.Callback
import kotlin.reflect.KClass
fun Node.hasClass(className: String) = styleClass.contains(className)
fun Node.addClass(className: String) = styleClass.add(className)
fun Node.removeClass(className: String) = styleClass.remove(className)
fun Node.toggleClass(className: String, predicate: Boolean) = if (predicate) removeClass(className) else addClass(className)
infix fun Node.addTo(pane: Pane) = pane.children.add(this)
fun Pane.replaceChildren(vararg uiComponents: UIComponent) =
this.replaceChildren(*(uiComponents.map { it.root }.toTypedArray()))
fun Pane.replaceChildren(vararg node: Node) {
children.clear()
children.addAll(node)
}
operator fun ToolBar.plusAssign(uiComponent: UIComponent): Unit {
items.add(uiComponent.root)
}
inline fun ToolBar.add(type: KClass): Unit = plusAssign(find(type))
operator fun Pane.plusAssign(node: Node) {
children.add(node)
}
inline fun Pane.add(type: KClass) = plusAssign(find(type).root)
operator fun Pane.plusAssign(type: KClass) = plusAssign(find(type).root)
operator fun Pane.plusAssign(view: UIComponent): Unit {
plusAssign(view.root)
}
fun GridPane.row(op: Pane.() -> Unit) {
userData = if (userData is Int) userData as Int + 1 else 1
// Allow the caller to add children to a fake pane
val fake = Pane()
op(fake)
// Create a new row in the GridPane and add the children added to the fake pane
addRow(userData as Int, *fake.children.toTypedArray())
}
val TableView.selectedItem: T
get() = this.selectionModel.selectedItem
fun TableView.selectFirst() = selectionModel.selectFirst()
val ListView.selectedItem: T
get() = selectionModel.selectedItem
fun TableView.onSelectionChange(func: (S?) -> Unit) =
selectionModel.selectedItemProperty().addListener({ observable, oldValue, newValue -> func(newValue) })
inline fun TableColumn.cellFormat(crossinline formatter: (TableCell.(T) -> Unit)) {
cellFactory = Callback { column: TableColumn ->
object : TableCell() {
override fun updateItem(item: T, empty: Boolean) {
super.updateItem(item, empty)
if (item == null || empty) {
text = null
graphic = null
} else {
formatter(this, item)
}
}
}
}
}
inline fun TableView.addColumn(title: String, crossinline valueProvider: (TableColumn.CellDataFeatures) -> ObservableValue): TableColumn {
val column = TableColumn(title)
column.cellValueFactory = Callback { valueProvider(it) }
columns.add(column)
return column
}
inline fun ListView.cellFormat(crossinline formatter: (ListCell.(T) -> Unit)) {
cellFactory = Callback {
object : ListCell() {
override fun updateItem(item: T, empty: Boolean) {
super.updateItem(item, empty)
if (item == null || empty) {
text = null
graphic = null
} else {
formatter(this, item)
}
}
}
}
}
/**
* Execute action when the enter key is pressed or the mouse is clicked
* @param clickCount The number of mouse clicks to trigger the action
* *
* @param action The action to execute on select
*/
fun TableView.onUserSelect(clickCount: Int = 2, action: (T) -> Unit) {
val isSelected = { event: InputEvent ->
event.target.isInsideTableRow() && !selectionModel.isEmpty
}
addEventFilter(MouseEvent.MOUSE_CLICKED) { event ->
if (event.clickCount == clickCount && isSelected(event))
action(selectedItem)
}
addEventFilter(KeyEvent.KEY_PRESSED) { event ->
if (event.code == KeyCode.ENTER && !event.isMetaDown && isSelected(event))
action(selectedItem)
}
}
fun TableView.onUserDelete(action: (T) -> Unit) {
addEventFilter(KeyEvent.KEY_PRESSED, { event ->
if (event.code == KeyCode.BACK_SPACE && selectedItem != null)
action(selectedItem)
})
}
fun ListView.onUserDelete(action: (T) -> Unit) {
addEventFilter(KeyEvent.KEY_PRESSED, { event ->
if (event.code == KeyCode.BACK_SPACE && selectedItem != null)
action(selectedItem)
})
}
/**
* Execute action when the enter key is pressed or the mouse is clicked
* @param clickCount The number of mouse clicks to trigger the action
* *
* @param action The runnable to execute on select
*/
fun ListView.onUserSelect(clickCount: Int = 2, action: (T) -> Unit) {
addEventFilter(MouseEvent.MOUSE_CLICKED) { event ->
if (event.clickCount == clickCount && selectedItem != null)
action(selectedItem)
}
addEventFilter(KeyEvent.KEY_PRESSED) { event ->
if (event.code == KeyCode.ENTER && !event.isMetaDown && selectedItem != null)
action(selectedItem)
}
}
/**
* Did the event occur inside a TableRow?
*/
fun EventTarget.isInsideTableRow(): Boolean {
if (this !is Node)
return false
if (this is TableColumnHeader)
return false
if (this is TableRow<*> || this is TableView<*>)
return true
if (this.parent != null)
return this.parent.isInsideTableRow()
return false
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy