Transcript
Page 1: JavaFX and Scala - Like Milk and Cookies

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

Meet the Presenters

Stephen Chin

Motorcyclist

Family Man

Luc Duponcheel

BeJUG

Researcher

Page 3: JavaFX and Scala - Like Milk and Cookies

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

4

JavaFX

Scala

Page 5: JavaFX and Scala - Like Milk and Cookies

JavaFX With Java

Page 6: JavaFX and Scala - Like Milk and Cookies

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

7

Vanishing Circles

Page 8: JavaFX and Scala - Like Milk and Cookies

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

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

Setup Binding

circle.strokeWidthProperty().bind(Bindings .when(circle.hoverProperty()) .then(4) .otherwise(0));

10

Page 11: JavaFX and Scala - Like Milk and Cookies

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

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

13

JavaFX With Scala

Page 14: JavaFX and Scala - Like Milk and Cookies

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

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

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

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

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

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

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

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

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

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

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

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

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

________ __ ________ __ __ / _____/\ / /\ / _____/\/__/\ / /\ / /\_____\/ ________ _______ / / / ________ / /\_____\/\ \ / / / / /_/___ / _____/\ /_____ /\ / / / /_____ /\ / /_/__ \ / / / /______ /\ / /\_____\/ \____/ / / / / / \____/ / / / ____/\ \/ / / \_____/ / / / / / / ___ / / / / / / ___ / / / /\____\/ / / /\ ______/ / / / /_/___ / /__/ / / / / / / /__/ / / / / / / / /\ \/________/ / /________/\ /________/ / /__/ / /________/ / /__/ / /__/ / \ \\________\/ \________\/ \________\/ \__\/ \________\/ \__\/ \__\/ \__\/

27

Jumping Frogs Puzzle

Image by renwest: http://www.flickr.com/photos/19941963@N00/438340463

Page 28: JavaFX and Scala - Like Milk and Cookies

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

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

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

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

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

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

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

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

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

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

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

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

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

@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

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

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

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

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/

Page 46: JavaFX and Scala - Like Milk and Cookies

46

Stephen [email protected]: @steveonjava

Pro JavaFX 2 Platform Available Now!


Top Related