为了能够更深入的了解JavaFX类库,我将会用Scala实现一个JavaFX的DSL。又开始挖大坑了,希望这次能够填完
这个DSL会是什么样呢?如果你对C++略有涉猎,那么肯定对C++中流插入和流提取不陌生。我们将要实现的这个DSL,就会围绕着<<插入符展开。
首先,我们面临的第一个问题,就是使用Scala的类继承Application类会报错(听说用单例对象继承就可以了,不过当时我没有想到这一点)。于是我写了自己的一个SApplication类作为所有Scala所写的JavaFX应用的基类:
package sjavafx.application
import javafx.application.Application
/**
* Created by Administrator on 2016/10/3.
*/
abstract class SApplication extends Application {
def run = Application.launch()
def run(args: Array[String]) = Application.launch(args: _*)
def run(args: String*) = Application.launch(args: _*)
def run(appClass: Class[_ <: Application], args: String*) = Application.launch(appClass, args: _*)
}
这个类只是Application类在Scala中简单的封装,这个类的子类将可以直接成为应用程序类而不需要再去写main()方法。
为了便于在构建DSL过程中进行调试,我从网络上(JavaFXChina)找到了一个简单的例子,然后拿Scala进行了小幅度的改写:
import sjavafx.application.SApplication
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.scene.layout.StackPane
import javafx.stage.Stage
class Text extends SApplication {
def start(primaryStage:Stage) {
val btn = new Button()
btn.setText("Say 'Hello World'")
btn.setOnAction(new EventHandler[ActionEvent]() {
def handle(event:ActionEvent) {
println("Hello World!")
}
})
val root = new StackPane()
root.getChildren().add(btn)
val scene = new Scene(root, 300, 250)
primaryStage.setTitle("Hello World!")
primaryStage.setScene(scene)
primaryStage.show()
}
}
现在我们就要先围绕着这个测试类来对JavaFX进行包装。
object SApplication {
}
首先我们在定义了一个SApplication类的伴生对象,里面将要存放一些十分重要的隐式转换等核心功能。
现在我们定义一个特质:Insertionable:
这个特质将是整个类库里最核心的部分之一。
object SApplication {
val VarPool = new mutable.HashMap[String, Insertionable]
object Name
}
然后我们在这个基础上就可以进行一定的修改了:
object SApplication {
val VarPool = new mutable.HashMap[String, Insertionable]
/**
* 用法:ins :Insertionable << Type << theType
* 描述:转换Insertionable类型变量为theType所描述类型.
*/
object Type
object tSButton/*sjavafx.scene.control.SButton*/
/**
* 用法:ins :Insertionable << Name << name:String
* 描述:设定变量名.
*/
object Name
}
trait Insertionable {
def <<(name: Name.type) = new Ins {
def <<(name: String) = {
VarPool(name) = ins
ins
}
}
def <<(ty: Type.type) = new Ins {
def <<(sbutton: tSButton.type) = ins match{
case sb :SButton => sb
case _ => throw new ClassCastException
}
}
class Ins(val ins: Insertionable = this)
}
然后我们加入了新的类SButton,这个类是Button类的子类,并实现了Insertionable接口:
class SButton() extends Button() with Insertionable {
def this(str: String) = {
this()
setText(str)
}
}
现在我们的测试类已经可以加入<<运算符了!虽然暂时还没有做什么实质性的事情,但是这也证明了我们的尝试是成功的:
import sjavafx.application.SApplication
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.layout.StackPane
import javafx.stage.Stage
import sjavafx.application.SApplication._
import sjavafx.scene.control.SButton
class Text extends SApplication {
def start(primaryStage:Stage) {
val btn = new SButton <<
Name << "btn" <<
Type << tSButton
btn.setText("Say 'Hello World'")
btn.setOnAction(new EventHandler[ActionEvent]() {
def handle(event:ActionEvent) {
println("Hello World!")
}
})
val root = new StackPane()
root.getChildren.add(btn)
val scene = new Scene(root, 300, 250)
primaryStage.setTitle("Hello World!")
primaryStage.setScene(scene)
primaryStage.show
}
}