FXML - ThoughtWorksInc/Binding.scala GitHub Wiki
Binding.scala 11 supports FXML and JavaFX.
You can create JavaFX GUI or any other JavaBeans, in FXML syntax, along with embedded Scala code with data-binding abilitiy.
import javafx.application.Application
import javafx.stage._
import javafx.scene._
import javafx.scene.control._
import com.thoughtworks.binding._
final class FxmlSample extends Application {
@fxml override def start(primaryStage: Stage): Unit = {
val scene: Binding[Scene] = <Scene><Label>Hello, World!</Label></Scene>
fxml.show(primaryStage, scene)
}
}
object FxmlSample {
def main(args: Array[String]): Unit = {
Application.launch(classOf[FxmlSample], args: _*)
}
}
Very simple!
Unlike FXML dynamically loaded from FXMLLoader
, Binding.scala compiles FXML into monadic expressions, which are statically type checked. Therefore, you can get rid of implicit conversion between XML text and other types, and use typed Scala expression instead.
@fxml val gridPane = {
<GridPane>
<Label text="My Label">
<!-- OK, as 2 is an Int -->
<GridPane.rowIndex>{2}</GridPane.rowIndex>
<!-- Compilation error because "not a number" is a string, not an Int -->
<GridPane.columnIndex>not a number</GridPane.columnIndex>
</Label>
</GridPane>
}
FXMLs loaded from FXMLLoader
support very limited data-binding expressions via ${...}
syntax. On the other hand, Binding.scala supports arbitrary Scala code as data-binding expressions.
@fxml def vbox(buttonText: BindingSeq[String]) = {
<VBox>
<Button text="first button"/>{
for (t <- buttonTexts) yield {
<Button text={t}/>
}
}<Button text="last button"/>
</VBox>
}
You can even create repeated UI elements in a for
/yield
block, whereby Button
s will be added and removed automatically whenever buttonTexts
changes.
@fxml
supports any JavaBean on both JVM and Scala.js. For example you can create custom JavaBeans in Scala.js:
@fxml val date = {
<?import scala.scalajs.js.Date?>
<Date date={2}>
<milliseconds>{42}</milliseconds>
</Date>
}
If you have used Binding.scala with Scala.js, you may already very familiar with @dom
.
@dom
creates a data-binding expression with XHTML support. For example:
@dom val i: Binding[Int] = 1
@dom def plusI(n: Binding[Int]): Binding[Int] = n.bind + i.bind
@dom val myDiv: Binding[HTMLDivElement] = {
val node: HTMLDivElement = <div></div>
myDiv
}
@dom val myDivs: Binding[BindingSeq[HTMLDivElement]] = {
val nodes: BindingSeq[HTMLDivElement] = <div></div><div></div>
nodes
}
Unlike @dom
, @fxml
itself does not create a root data-binding expression for the annotated method, it merely converts FXML literals into Binding
or BindingSeq
.
@fxml val i: Int = 1 // No magic here, i is not bindable
// Compilation error because n.bind is not inside a data-binding context
// @fxml def plusI(n: Binding[Int]): Int = n.bind + i
// Manually create a Binding block in order to use `.bind` syntax.
@fxml def plusI(n: Binding[Int]): Binding[Int] = Binding { n.bind + i }
@fxml val myButton: Binding[Button] = {
val node: Binding[Button] = <Button/>
node
}
@fxml val myButtons: BindingSeq[Button] = {
val nodes: BindingSeq[Button] = <Button/><Button/>
nodes
}