package uk.co.nickthecoder.glok.demos
import uk.co.nickthecoder.glok.application.Application
import uk.co.nickthecoder.glok.application.launch
import uk.co.nickthecoder.glok.control.FormGrid
import uk.co.nickthecoder.glok.control.FormRow
import uk.co.nickthecoder.glok.control.Label
import uk.co.nickthecoder.glok.control.ScrollPane
import uk.co.nickthecoder.glok.demos.DemoFormGrid.Companion.ABOUT
import uk.co.nickthecoder.glok.property.boilerplate.ObservableBoolean
import uk.co.nickthecoder.glok.property.functions.isEmpty
import uk.co.nickthecoder.glok.property.functions.not
import uk.co.nickthecoder.glok.scene.Edges
import uk.co.nickthecoder.glok.scene.Stage
import uk.co.nickthecoder.glok.scene.dsl.*
import uk.co.nickthecoder.glok.theme.styles.ERROR
/**
* See [ABOUT].
*/
class DemoFormGrid : Application() {
lateinit var form: FormGrid
override fun start(primaryStage: Stage) {
with(primaryStage) {
title = "DemoFormGrid"
scene {
root = demoPanel {
name = "DemoFormGrid"
about = ABOUT
relatedDemos.addAll(DemoTantalum::class, DemoFormGridJoined::class)
content = borderPane {
center = scrollPane {
fitToHeight = true
fitToWidth = true
content = form {
section = true
form = this
padding = Edges(10f)
rowSpacing = 20f
+ row { // See the next row for a "better" way to set the left label.
left = Label("Name")
right = textField("") { // Just here to demo promptText
prefColumnCount =
20 // By setting growPriority, this will extend to the right of the FormGrid
growPriority = 1f // Adds a validation check to the row.
errors = errorMessage(
"Required",
textProperty.isEmpty()
)
// Note `errors` is deliberately set within the `textField` block of code.
// In this block, we have many "receivers". a TextArea, a FormRow, a GridForm...
// The line above could be written more verbosely as :
//
// this@row.errors = this@DemoFormGrid.errorMessage("Required", this.textProperty.isEmpty())
//
// Which highlights the 3 different receivers being used.
}
}
// As `left` is very often a Label, there's a convenience function for this case :
+ row("Phone Number") {
right = textField("") {
// Note, we have asked for the same width as "Name", but have not set growPriority.
// Resize the window to see the difference.
prefColumnCount = 20
}
}
+ row("Click to agree") {
above = Label("I have read, and agree with the terms and conditions")
right = checkBox {
errors =
errorMessage("You must agree before before continuing", ! selectedProperty)
}
}
}
}
bottom = hBox {
padding(4)
+ spacer()
+ button("OK") {
onAction {
val errorRow: FormRow? = form.rows.firstOrNull { it.errors?.visible == true }
if (errorRow == null) { // Validation passed, so update the database???
scene?.stage?.close()
} else {
// Look for a ScrollPane (if there is one), and ensure the error row is visible.
// Using grandparent, because the form's parent is the ScrollPane's private Viewport node.
val grandparent = form.parent?.parent
if (grandparent is ScrollPane) {
grandparent.scrollTo(errorRow)
}
// This will focus on the TextArea that is invalid.
errorRow.requestFocus(useFocusTraversable = true, showFocusBorder = true)
}
}
}
}
}
}
}
show()
}
}
private fun errorMessage(text: String, error: ObservableBoolean) =
label(text) {
style(ERROR)
visibleProperty.bindTo(error)
}
companion object {
private val ABOUT = """
[FormGrid] is specifically designed to make laying out forms easy,
much easier than using [GridPane]. I hate using JavaFX's [GridPane].
A [FromGrid] has a list of rows ([FormRow]), and each row has up to 4 nodes :
`left` : Usually used for a [Label]
`right` : Usually used for a [TextField], [Spinner], [CheckBox], etc.)
`above` : Less frequently used. Can be used instead of `left` and `right`.
`below` : Information about the row.
`errors` : To display error messages when the input is invalid.
The `right` columns line up with each other.
`above`, `below` and `errors` take up the full width of the row, and do not affect the
position of the `right` Nodes.
PS. There's another reason not to use [GridPane], I haven't written it yet! ;-))
This demo includes simple validation checks.
Rows which require validation set the row's `errors` content to a Label,
which is automatically made visible/invisible based on an ObservableValue.
The +OK+ button only needs to check if any row's `errors` content is visible.
If it is, do _NOT_ process the form, and instead, requestFocus on the `right` of the row.
Note, this functionality is not part of [FormGrid] or [FormRow], and you can
implement a more comprehensive validation system if you want.
""".trimIndent()
@JvmStatic
fun main(vararg args: String) {
launch(DemoFormGrid::class)
}
}
}