JavaFX and Scala – Like Milk and Cookies    Stephen Chin                Luc Duponcheel    JavaFX Evangelist, Oracle   Inde...
Meet the Presenters        Stephen Chin                 Luc Duponcheel  Family Man                 Motorcyclist           ...
JavaFX 2.0 PlatformImmersive Application Experience>   Cross-platform    Animation, Video, Charting>   Integrate Java, Jav...
JavaFXScala                 4
JavaFX With Java
JavaFX in Java>   JavaFX API uses an enhanced JavaBeans    pattern>   Similar in feel to other UI toolkits (Swing,    Pivo...
Vanishing Circles                    7
Application Skeletonpublic class VanishingCircles extends Application {  public static void main(String[] args) {    Appli...
Create the CirclesList<Circle> circles = new ArrayList<Circle>();for (int i = 0; i < 50; i++) {  final Circle circle = new...
Setup Bindingcircle.strokeWidthProperty().bind(Bindings   .when(circle.hoverProperty())   .then(4)   .otherwise(0));      ...
Setup Event Listenerscircle.addEventHandler(MouseEvent.MOUSE_CLICKED,                        new EventHandler<MouseEvent>(...
Begin the AnimationTimeline moveCircles = new Timeline();for (Circle circle : circles) {  KeyValue moveX = new KeyValue(ci...
JavaFX With Scala               13
Java vs. Scala DSLpublic class VanishingCircles extends Application {                                   object VanishingCi...
object VanishingCircles extends JFXApp {  stage = new Stage {    title = "Disappearing Circles"    width = 800    height =...
object VanishingCircles extends JFXApp {  stage = new Stage {    title = "Disappearing Circles"    width = 800    height =...
object VanishingCircles extends JFXApp {  stage = new Stage {    title = "Disappearing Circles"    width = 800    height =...
object VanishingCircles extends JFXApp {  stage = new Stage {    title = "Disappearing Circles"    width = 800            ...
object VanishingCircles extends JFXApp {  stage = new Stage {    title = "Disappearing Circles"    width = 800            ...
Binding in ScalaInfix Addition/Subtraction/Multiplication/Division:height <== rect1.height + rect2.heightAggregate Operato...
Animation in Scalaval timeline = new Timeline {  cycleCount = INDEFINITE  autoReverse = true  keyFrames = for (circle <- c...
JavaFX Script-like animationAnimation in Scala       syntax: at (duration) {keyframes}val timeline = new Timeline {  cycle...
Animation in Scalaval timeline = new Timeline {  cycleCount = INDEFINITE  autoReverse = true  keyFrames = for (circle <- c...
Animation in Scalaval timeline = new Timeline {  cycleCount = INDEFINITE  autoReverse = true  keyFrames = for (circle <- c...
Event Listeners in Scala>   Supported using the built-in Closure syntax>   Arguments for event objects>   100% type-safe  ...
Event Listeners in Scala>   Supported using the built-in Closure syntax>   Arguments for event objects          Optional e...
Jumping Frogs PuzzleImage by renwest: http://www.flickr.com/photos/19941963@N00/438340463                                 ...
Jumping Frogs Puzzle>   My first ScalaFX program>   Similar to (but more concise as) my version using    JavaFX 1.2>   Aft...
Applicationobject JumpingFrogsPuzzle extends JFXApp {  stage = new Stage {    title = TITLE    scene = new Scene {    cont...
Frogtrait   Frog {  def   movesToRight: Boolean  def   movesToLeft: Boolean}class   LeftFrog() extends Frog {  def   moves...
Model (frogMap)var frogMap: Map[Int, Frog] = _private val frogAtPosition = (i: Int) =>  frogMap(i)val positionOf = (frog: ...
Model (move)private val move =  (next: Int => Int) => (frog: Frog) =>  frogMap = (for {    entry@(_, aFrog) <- frogMap    ...
View (jump)private val jump =  (length: Int) =>  (direction: (Double, Double) => Double) =>  (frogShape: FrogShape) => {  ...
View (control)private val control: Unit => Unit = { _ =>  view.frogShapes.foreach {    frogShape => frogShape.onMouseClick...
ScalaFX Internals a.k.a. How to Write Your Own Scala DSLWith quotes from Stephen Colebourne (@jodastephen) to helpus keep ...
Application Initialization>   JavaFX Requires all UI code executed on the    Application Thread>   But our ScalaFX Applica...
DelayedInit>    Introduced in Scala 2.9>    How to Use It:1.    Extend a special trait called DelayedInit2.    Implement a...
Hierarchical Implicit Conversions>   ScalaFX defines a set of proxies that mirror the    JavaFX hierarchy>   JavaFX classe...
N-Level Implicit Precedence>   Scala throws an exception if two implicits have the    same precedence>   Classes that are ...
Properties>   JavaFX supports properties of type Boolean,    Integer, Long, Float, Double, String, and Object>   Propertie...
@specialized>   Special annotation that generates primitive    variants of the class>   Improves performance by avoiding  ...
Bindings>    How does Scala know what order to evaluate this     in?text <== when (rect.hover || circle.hover&& !disabled)...
Operator Precedence Rules>   First Character Determines Precedence    Lowest Precedence       10. (all   letters)     Exce...
Operator Precedencetext <== when (rect.hover || circle.hover            11       10                               9&& !dis...
Conclusion>   You can use Scala and JavaFX together.>   ScalaFX provides cleaner APIs that are tailor    designed for Scal...
Stephen Chin                     steveonjava@gmail.com                     tweet: @steveonjavaPro JavaFX 2 Platform Availa...
Upcoming SlideShare
Loading in...5
×

JavaFX and Scala - Like Milk and Cookies

6,480

Published on

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.

Published in: Technology
1 Comment
4 Likes
Statistics
Notes
No Downloads
Views
Total Views
6,480
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
64
Comments
1
Likes
4
Embeds 0
No embeds

No notes for slide
  • Stage.add??
  • JavaFX and Scala - Like Milk and Cookies

    1. 1. JavaFX and Scala – Like Milk and Cookies Stephen Chin Luc Duponcheel JavaFX Evangelist, Oracle Independent, ImagineJ steveonjava@gmail.com luc.duponcheel@gmail.com tweet: @steveonjava tweet: @LucDup
    2. 2. Meet the Presenters Stephen Chin Luc Duponcheel Family Man Motorcyclist BeJUG Researcher
    3. 3. JavaFX 2.0 PlatformImmersive 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.
    4. 4. JavaFXScala 4
    5. 5. JavaFX With Java
    6. 6. 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
    7. 7. Vanishing Circles 7
    8. 8. Application Skeletonpublic 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…] }}
    9. 9. Create the CirclesList<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
    10. 10. Setup Bindingcircle.strokeWidthProperty().bind(Bindings .when(circle.hoverProperty()) .then(4) .otherwise(0)); 10
    11. 11. Setup Event Listenerscircle.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
    12. 12. Begin the AnimationTimeline 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
    13. 13. JavaFX With Scala 13
    14. 14. Java vs. Scala DSLpublic class VanishingCircles extends Application { object VanishingCircles extends JFXApp { var circles: Seq[Circle] = null public static void main(String[] args) { stage = new Stage { Application.launch(args); title = "Vanishing Circles" } width = 800 height = 600 @Override scene = new Scene { public void start(Stage primaryStage) { fill = BLACK primaryStage.setTitle("Vanishing Circles"); circles = for (i <- 0 until 50) yield new Circle { Group root = new Group(); centerX = random * 800 Scene scene = new Scene(root, 800, 600, Color.BLACK); centerY = random * 600 List<Circle> circles = new ArrayList<Circle>(); radius = 150 for (int i = 0; i < 50; i++) { fill = color(random, random, random, .2) final Circle circle = new Circle(150); effect = new BoxBlur(10, 10, 3) 40 Lines 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)); 33 Lines strokeWidth <== when (hover) then 4 otherwise 0 stroke = WHITE onMouseClicked = { Timeline(at (3 s) {radius -> 0}).play() 1299 Characters circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); } } } 591 Characters content = circles new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); } } }); new Timeline { circle.setStroke(Color.WHITE); cycleCount = INDEFINITE circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) autoReverse = true .then(4) keyFrames = for (circle <- circles) yield at (40 s) { .otherwise(0)); Set( circles.add(circle); circle.centerX -> random * stage.width, } circle.centerY -> random * stage.height root.getChildren().addAll(circles); ) primaryStage.setScene(scene); } primaryStage.show(); }.play(); } 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(); }} 14
    15. 15. 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
    16. 16. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 class for JavaFX Base applications 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) } } }} 16
    17. 17. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 Declarative Stage scene = new Scene { fill = BLACK definition 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) } } }} 17
    18. 18. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 Inline property height = 600 definitions 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) } } }} 18
    19. 19. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 Sequence Creation Via height = 600 Loop 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) } } }} 19
    20. 20. Binding in ScalaInfix Addition/Subtraction/Multiplication/Division:height <== rect1.height + rect2.heightAggregate Operators:width <== max(rect1.width, rect2.width, rect3.width)Conditional Expressions:strokeWidth <== when (hover) then 4 otherwise 0Compound Expressions:text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled" otherwise "disabled" 20
    21. 21. Animation in Scalaval 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
    22. 22. JavaFX Script-like animationAnimation in Scala syntax: at (duration) {keyframes}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(); 22
    23. 23. Animation in Scalaval 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(); Operator overloading for animation syntax 23
    24. 24. Animation in Scalaval 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(); Optional tween syntax 24
    25. 25. Event Listeners in Scala> Supported using the built-in Closure syntax> Arguments for event objects> 100% type-safe onMouseClicked = { (e: MouseEvent) => Timeline(at(3 s){radius->0}).play() } 25
    26. 26. Event Listeners in Scala> Supported using the built-in Closure syntax> Arguments for event objects Optional event> 100% type-safe parameter {(event) => body} onMouseClicked = { (e: MouseEvent) => Timeline(at(3 s){radius->0}).play() } 26
    27. 27. Jumping Frogs PuzzleImage by renwest: http://www.flickr.com/photos/19941963@N00/438340463 27
    28. 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) 28
    29. 29. Applicationobject JumpingFrogsPuzzle extends JFXApp { stage = new Stage { title = TITLE scene = new Scene { content = theShapes.canvasShape :: theShapes.stoneShapes ::: theView.frogShapes } }} 29
    30. 30. Frogtrait 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
    31. 31. 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
    32. 32. 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
    33. 33. 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
    34. 34. 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
    35. 35. ScalaFX Internals a.k.a. How to Write Your Own Scala DSLWith quotes from Stephen Colebourne (@jodastephen) to helpus keep our sanity!Disclaimer: Statements taken from http://blog.joda.org and may not accurately reflect his opinion or viewpoint. 35
    36. 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?!? 36
    37. 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 Joda says… For me, Scala didnt throw enough away and added too much - a lethal combination. 37
    38. 38. 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! JFXNode SFXNode JFXShape ? SFXShape JFXCircle ! SFXCircle 38
    39. 39. N-Level Implicit Precedence> Scala throws an exception if two implicits have the same precedence> Classes that are extended have 1 lower precedence:object HighPriorityIncludes extends LowerPriorityIncludes {…}trait LowerPriorityIncludes {…}> You can stack extended traits n-levels deep to reduce precision by n Joda says… Well, it may be type safe, but its also silent and very deadly. 39
    40. 40. Properties> JavaFX supports properties of type Boolean, Integer, Long, Float, Double, String, and Object> Properties use Generics for type safety> But generics dont support primitives…> JavaFX solves this with 20 interfaces and 44 classes for all the type/readable/writable combinations.> Can we do better? 40
    41. 41. @specialized> Special annotation that generates primitive variants of the class> Improves performance by avoiding boxing/unboxing trait ObservableValue[@specialized(Int, Long, Float, Double, Boolean) T, J]> Cuts down on code duplication (ScalaFX only has 18 property/value classes total) Joda says… Whatever the problem, the type system is bound to be part of the solution. 41
    42. 42. Bindings> How does Scala know what order to evaluate this in?text <== when (rect.hover || circle.hover&& !disabled) then textField.text + " isenabled" otherwise "disabled And why the funky bind operator?!? 42
    43. 43. Operator Precedence Rules> First Character Determines Precedence Lowest Precedence 10. (all letters) Exception Assignment 9. | Operators, which are 8. ^ even lower… 7. & 6. < > 11. Assignment Operators 5. = ! end with equal 4. : > But dont start with equal 3. + * > And cannot be one of:  <= 2. / %  >= 1. (all other special  != characters) Highest Precedence 43
    44. 44. Operator Precedencetext <== when (rect.hover || circle.hover 11 10 9&& !disabled) then textField.text + " is7 5 10 3enabled" otherwise "disabled" 10 Joda says… Personally, I find the goal of the open and flexible syntax (arbitrary DSLs) to be not worth the pain 44
    45. 45. 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/
    46. 46. Stephen Chin steveonjava@gmail.com tweet: @steveonjavaPro JavaFX 2 Platform Available Now! 46
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×