javafx and scala - like milk and cookies
DESCRIPTION
Presentation on Scala and JavaFX given at Scala Days. Shows how the ScalaFX API can be used to write cleaner and more maintainable code for your JavaFX applications in the Scala language. Also goes over implementation details that may be useful to other Scala DSL creators and has some quotes from Stephen Coulbourne to "lighten" things up.TRANSCRIPT
![Page 1: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/1.jpg)
JavaFX and Scala – Like Milk and Cookies
Luc DuponcheelIndependent, [email protected]: @LucDup
Stephen ChinJavaFX Evangelist, [email protected]: @steveonjava
![Page 2: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/2.jpg)
Meet the Presenters
Stephen Chin
Motorcyclist
Family Man
Luc Duponcheel
BeJUG
Researcher
![Page 3: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/3.jpg)
JavaFX 2.0 Platform
Immersive Application Experience
> Cross-platform Animation, Video, Charting
> Integrate Java, JavaScript, and HTML5 in the same application
> New graphics stack takes advantage of hardware acceleration for 2D and 3D applications
> Use your favorite IDE: NetBeans, Eclipse, IntelliJ, etc.
![Page 4: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/4.jpg)
4
JavaFX
Scala
![Page 5: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/5.jpg)
JavaFX With Java
![Page 6: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/6.jpg)
JavaFX in Java
> JavaFX API uses an enhanced JavaBeans pattern
> Similar in feel to other UI toolkits (Swing, Pivot, etc.)
> Uses builder pattern to minimize boilerplate
![Page 7: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/7.jpg)
7
Vanishing Circles
![Page 8: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/8.jpg)
Application Skeleton
public class VanishingCircles extends Application { public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Vanishing Circles"); Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); [create the circles…] root.getChildren().addAll(circles); primaryStage.setScene(scene); primaryStage.show(); [begin the animation…] }}
![Page 9: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/9.jpg)
Create the Circles
List<Circle> circles = new ArrayList<Circle>();for (int i = 0; i < 50; i++) { final Circle circle = new Circle(150); circle.setCenterX(Math.random() * 800); circle.setCenterY(Math.random() * 600); circle.setFill(new Color(Math.random(), Math.random(), Math.random(), .2)); circle.setEffect(new BoxBlur(10, 10, 3)); circle.setStroke(Color.WHITE); [setup binding…] [setup event listeners…] circles.add(circle);}
9
![Page 10: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/10.jpg)
Setup Binding
circle.strokeWidthProperty().bind(Bindings .when(circle.hoverProperty()) .then(4) .otherwise(0));
10
![Page 11: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/11.jpg)
Setup Event Listeners
circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); }});
11
![Page 12: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/12.jpg)
Begin the Animation
Timeline moveCircles = new Timeline();for (Circle circle : circles) { KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random() * 800); KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random() * 600); moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40), moveX, moveY));}moveCircles.play();
12
![Page 13: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/13.jpg)
13
JavaFX With Scala
![Page 14: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/14.jpg)
Java vs. Scala DSLpublic class VanishingCircles extends Application {
public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Vanishing Circles"); Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); List<Circle> circles = new ArrayList<Circle>(); for (int i = 0; i < 50; i++) { final Circle circle = new Circle(150); circle.setCenterX(Math.random() * 800); circle.setCenterY(Math.random() * 600); circle.setFill(new Color(Math.random(), Math.random(),
Math.random(), .2)); circle.setEffect(new BoxBlur(10, 10, 3)); circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new
EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); } }); circle.setStroke(Color.WHITE);
circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) .then(4) .otherwise(0)); circles.add(circle); } root.getChildren().addAll(circles); primaryStage.setScene(scene); primaryStage.show(); Timeline moveCircles = new Timeline(); for (Circle circle : circles) { KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random()
* 800); KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random()
* 600); moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40),
moveX, moveY)); } moveCircles.play(); }}
object VanishingCircles extends JFXApp { var circles: Seq[Circle] = null stage = new Stage { title = "Vanishing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK circles = for (i <- 0 until 50) yield new Circle { centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, .2) effect = new BoxBlur(10, 10, 3) strokeWidth <== when (hover) then 4 otherwise 0 stroke = WHITE onMouseClicked = { Timeline(at (3 s) {radius -> 0}).play() } } content = circles } }
new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) { Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) } }.play();}
14
40 Lines1299 Characters
33 Lines591 Characters
![Page 15: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/15.jpg)
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
15
![Page 16: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/16.jpg)
16
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle { centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Base class for JavaFX applications
![Page 17: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/17.jpg)
17
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Declarative Stage definition
![Page 18: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/18.jpg)
18
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Inline property definitions
![Page 19: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/19.jpg)
19
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Sequence Creation Via Loop
![Page 20: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/20.jpg)
Binding in Scala
Infix Addition/Subtraction/Multiplication/Division:
height <== rect1.height + rect2.height
Aggregate Operators:
width <== max(rect1.width, rect2.width, rect3.width)
Conditional Expressions:
strokeWidth <== when (hover) then 4 otherwise 0
Compound Expressions:
text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled" otherwise "disabled"
20
![Page 21: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/21.jpg)
Animation in Scala
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) }}timeline.play();
21
![Page 22: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/22.jpg)
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) }}timeline.play();
Animation in Scala
22
JavaFX Script-like animation syntax: at (duration) {keyframes}
![Page 23: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/23.jpg)
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) }}timeline.play();
Animation in Scala
23
Operator overloading for animation syntax
![Page 24: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/24.jpg)
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width tween EASE_BOTH,
circle.centerY -> random * stage.height tween EASE_IN
) }}timeline.play();
Animation in Scala
24
Optional tween syntax
![Page 25: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/25.jpg)
Event Listeners in Scala
25
> Supported using the built-in Closure syntax> Arguments for event objects> 100% type-safe
onMouseClicked = { (e: MouseEvent) =>
Timeline(at(3 s){radius->0}).play()
}
![Page 26: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/26.jpg)
Event Listeners in Scala
> Supported using the built-in Closure syntax> Arguments for event objects> 100% type-safe
26
Optional event parameter
{(event) => body}
onMouseClicked = { (e: MouseEvent) =>
Timeline(at(3 s){radius->0}).play()
}
![Page 27: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/27.jpg)
________ __ ________ __ __ / _____/\ / /\ / _____/\/__/\ / /\ / /\_____\/ ________ _______ / / / ________ / /\_____\/\ \ / / / / /_/___ / _____/\ /_____ /\ / / / /_____ /\ / /_/__ \ / / / /______ /\ / /\_____\/ \____/ / / / / / \____/ / / / ____/\ \/ / / \_____/ / / / / / / ___ / / / / / / ___ / / / /\____\/ / / /\ ______/ / / / /_/___ / /__/ / / / / / / /__/ / / / / / / / /\ \/________/ / /________/\ /________/ / /__/ / /________/ / /__/ / /__/ / \ \\________\/ \________\/ \________\/ \__\/ \________\/ \__\/ \__\/ \__\/
27
Jumping Frogs Puzzle
Image by renwest: http://www.flickr.com/photos/19941963@N00/438340463
![Page 28: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/28.jpg)
28
Jumping Frogs Puzzle
> My first ScalaFX program> Similar to (but more concise as) my version using
JavaFX 1.2> After a few JavaFX 1.2 to ScalaFX translation
iterations it just worked> After some MVC rethinking and a few refactoring
iterations it worked even better (e.g. it is possible to click on a jumping frog)
![Page 29: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/29.jpg)
Application
object JumpingFrogsPuzzle extends JFXApp { stage = new Stage { title = TITLE scene = new Scene { content = theShapes.canvasShape :: theShapes.stoneShapes ::: theView.frogShapes } }}
29
![Page 30: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/30.jpg)
Frog
trait Frog { def movesToRight: Boolean def movesToLeft: Boolean}class LeftFrog() extends Frog { def movesToRight = true def movesToLeft = false}class RightFrog() extends Frog { def movesToRight = false def movesToLeft = true}
30
![Page 31: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/31.jpg)
Model (frogMap)
var frogMap: Map[Int, Frog] = _
private val frogAtPosition = (i: Int) => frogMap(i)
val positionOf = (frog: Frog) => (for { (i, `frog`) <- frogMap} yield i) head
31
![Page 32: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/32.jpg)
Model (move)
private val move = (next: Int => Int) => (frog: Frog) => frogMap = (for { entry@(_, aFrog) <- frogMap if (aFrog != frog) } yield { entry }) + (next(positionOf(frog)) -> frog)
32
![Page 33: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/33.jpg)
View (jump)
private val jump = (length: Int) => (direction: (Double, Double) => Double) => (frogShape: FrogShape) => { // ... Timeline(Seq( at(midTime) { frogShape.centerX -> midCenterX }, at(midTime) { frogShape.centerY -> midCenterY }, at(endTime) { frogShape.centerX -> endCenterX }, at(endTime) { frogShape.centerY -> endCenterY } )).play()}
33
![Page 34: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/34.jpg)
View (control)
private val control: Unit => Unit = { _ => view.frogShapes.foreach { frogShape => frogShape.onMouseClicked = { (_: MouseEvent) => val frog = frogShape.frog if (model.canMoveOneRight(frog)) { view.jumpOneRight(frogShape) model.moveOneRight(frog) } else if (model.canMoveTwoRight(frog)) { view.jumpTwoRight(frogShape) model.moveTwoRight(frog)
34
![Page 35: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/35.jpg)
a.k.a. How to Write Your Own Scala DSL
35
ScalaFX Internals
With quotes from Stephen Colebourne (@jodastephen) to help us keep our sanity!Disclaimer: Statements taken from http://blog.joda.org and may not accurately reflect his opinion or viewpoint.
Luc Viatour / www.Lucnix.be
![Page 36: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/36.jpg)
36
Application Initialization
> JavaFX Requires all UI code executed on the Application Thread
> But our ScalaFX Application has no start method:
object VanishingCircles extends JFXApp {
stage = new Stage { … }}
How Does This Code Work?!?
![Page 37: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/37.jpg)
37
DelayedInit
> Introduced in Scala 2.9> How to Use It:1. Extend a special trait called DelayedInit2. Implement a method of type:
def delayedInit(x: => Unit): Unit3. Store off the init closure and call it on the
Application Thread
For me, Scala didn't throw enough away and added too much - a lethal combination.
Joda says…
![Page 38: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/38.jpg)
Hierarchical Implicit Conversions
> ScalaFX defines a set of proxies that mirror the JavaFX hierarchy
> JavaFX classes are "implicitly" wrapped when you call a ScalaFX API
> But Scala implicit priority ignores type hierarchy!
38
JFXNode
JFXShape
JFXCircle
SFXNode
SFXShape
SFXCircle
?
!
![Page 39: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/39.jpg)
N-Level Implicit Precedence
> Scala throws an exception if two implicits have the same precedence
> Classes that are extended have 1 lower precedence:
> You can stack extended traits n-levels deep to reduce precision by n
39
Well, it may be type safe, but its also silent and very deadly.
Joda says…
object HighPriorityIncludes extends LowerPriorityIncludes {…}trait LowerPriorityIncludes {…}
![Page 40: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/40.jpg)
Properties
> JavaFX supports properties of type Boolean, Integer, Long, Float, Double, String, and Object
> Properties use Generics for type safety> But generics don't support primitives…
> JavaFX solves this with 20 interfaces and 44 classes for all the type/readable/writable combinations.
> Can we do better?
40
![Page 41: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/41.jpg)
@specialized
> Special annotation that generates primitive variants of the class
> Improves performance by avoiding boxing/unboxing
> Cuts down on code duplication (ScalaFX only has 18 property/value classes total)
41
Whatever the problem, the type system is bound to be part of the solution.
Joda says…
trait ObservableValue[@specialized(Int, Long, Float, Double, Boolean) T, J]
![Page 42: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/42.jpg)
Bindings
42
> How does Scala know what order to evaluate this in?
text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled" otherwise "disabled
And why the funky bind operator?!?
![Page 43: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/43.jpg)
Operator Precedence Rules
43
> First Character Determines Precedence
Highest Precedence
10. (all letters)9. |8. ^7. &6. < >5. = !4. :3. + *2. / %1. (all other special characters)
Lowest Precedence
11. Assignment Operators end with equal> But don't start with equal> And cannot be one of:
<= >= !=
Exception Assignment Operators, which are even lower…
![Page 44: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/44.jpg)
Operator Precedence
44
Personally, I find the goal of the open and flexible syntax (arbitrary DSLs) to be not worth the pain
Joda says…
text <== when (rect.hover || circle.hover
&& !disabled) then textField.text + " is
enabled" otherwise "disabled"
911 10
7 105 3
10
![Page 45: JavaFX and Scala - Like Milk and Cookies](https://reader033.vdocuments.us/reader033/viewer/2022061218/54b6d3384a7959703e8b459a/html5/thumbnails/45.jpg)
Conclusion
> You can use Scala and JavaFX together.> ScalaFX provides cleaner APIs that are tailor
designed for Scala.> Try using ScalaFX today and help contribute APIs
for our upcoming 1.0 release!
http://code.google.com/p/scalafx/