SlideShare a Scribd company logo
Hacking JavaFX with Groovy, Clojure,
Scala, and Visage
                       Stephen Chin
                       Java Evangelist, Oracle
                       stephen.chin@oracle.com
                       tweet: @steveonjava
Meet the Presenter

           Stephen Chin
                                     >   Java Evangelist, Oracle
                                     >   Author, Pro JavaFX Platform 2
      Family Man
                                     >   Open Source Hacker
                                            JFXtras
                                            ScalaFX
                      Motorcyclist          Visage
                                     >   User Group Co-Leader
                                            Silicon Valley JavaFX
                                             User Group
                                            Streamed Live!
Safe Harbor Statement
The following is intended to outline our general product
direction. It is intended for information purposes only, and
may not be incorporated into any contract. It is not a
commitment to deliver any material, code, or functionality,
and should not be relied upon in making purchasing
decisions. The development, release, and timing of any
features or functionality described for Oracle’s products
remains at the sole discretion of Oracle.
JavaFX 2.0 Platform
Immersive Application Experience
Leverage your Java skills with modern JavaFX
APIs
>   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.
JavaFX With Java
Programming Languages
>   JavaFX 2.0 APIs are now in Java
       Pure Java APIs for all of JavaFX
       Binding and Sequences exposed as Java APIs
       FXML Markup for tooling
>   Embrace all JVM languages
       Groovy, Scala, Clojure, JRuby
       Fantom, Mira, Gosu, Jython, etc.
>   JavaFX Script is no longer supported by Oracle
       Existing JavaFX Script based applications will continue to run
       Visage is the open-source successor to the JavaFX Script language
Vanishing Circles




                    7
Vanishing Circles in Java
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);
      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));
                                                                                              40 Lines
        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);
                                                                                              1299 Characters
            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();
    }
}




                                                                                                                8
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…]
  }
}
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);
}

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




                                             11
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();
  }
});




                                                                    12
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();


                                                                  13
JavaFX With Groovy
Features of Groovy
>   Modern language
       Closures
       AST Transforms
       Strongly typed dynamic language

>   Tight integration with Java
       Very easy to port from Java to Groovy

>   Declarative syntax with GroovyFX Builders
       Familiar to Groovy and JavaFX Script developers
Java vs. GroovyFX DSL
public class VanishingCircles extends Application {                                           GroovyFX.start { primaryStage ->
                                                                                                def sg = new SceneGraphBuilder()
    public static void main(String[] args) {                                                    def rand = new Random().&nextInt
      Application.launch(args);                                                                 def circles = []
    }
                                                                                                  sg.stage(title: 'Vanishing Circles', show: true) {
    @Override                                                                                       scene(fill: black, width: 800, height: 600) {
    public void start(Stage primaryStage) {                                                           50.times {
      primaryStage.setTitle("Vanishing Circles");                                                       circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white,
      Group root = new Group();                                                                                  strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
      Scene scene = new Scene(root, 800, 600, Color.BLACK);                                               fill rgb(rand(255), rand(255), rand(255), 0.2)
      List<Circle> circles = new ArrayList<Circle>();                                                     effect boxBlur(width: 10, height: 10, iterations: 3)
      for (int i = 0; i < 50; i++) {                                                                      onMouseClicked { e ->


                                                                                                                     29 Lines
        final Circle circle = new Circle(150);                                                              timeline {

                  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));                                                       }
                                                                                                          }
                                                                                                              at(3.s) { change e.source.radiusProperty() to 0 }
                                                                                                            }.play()




                  1299 Characters
        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();
                                                                                                    }
                                                                                                      }
                                                                                                                     671 Characters
                                                                                                      timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) {
          }                                                                                             circles.each { circle ->
        });                                                                                               at (40.s) {
        circle.setStroke(Color.WHITE);                                                                      change circle.centerXProperty() to rand(800)
        circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty())                             change circle.centerYProperty() to rand(600)
          .then(4)                                                                                        }
          .otherwise(0));                                                                               }
        circles.add(circle);                                                                          }.play()
      }                                                                                           }
      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();
    }
}




                                                                                                                                                                                                16
GroovyFX.start { primaryStage ->
  def sg = new SceneGraphBuilder()
  def rand = new Random().&nextInt
  def circles = []

    sg.stage(title: 'Vanishing Circles', show: true) {
      scene(fill: black, width: 800, height: 600) {
        50.times {
          circles << circle(centerX: rand(800), centerY: rand(600),
              radius: 150, stroke: white,
              strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
            fill rgb(rand(255), rand(255), rand(255), 0.2)
            effect boxBlur(width: 10, height: 10, iterations: 3)
          }
        }
      }
    }
}
                                                                          17
GroovyFX.start { primaryStage ->
  def sg = new SceneGraphBuilder()
  def rand = new Random().&nextInt
  def circles = []
                                       Builder for GroovyFX scene graphs
    sg.stage(title: 'Vanishing Circles', show: true) {
      scene(fill: black, width: 800, height: 600) {
        50.times {
          circles << circle(centerX: rand(800), centerY: rand(600),
              radius: 150, stroke: white,
              strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
            fill rgb(rand(255), rand(255), rand(255), 0.2)
            effect boxBlur(width: 10, height: 10, iterations: 3)
          }
        }
      }
    }
}
                                                                          18
GroovyFX.start { primaryStage ->
  def sg = new SceneGraphBuilder()
  def rand = new Random().&nextInt
  def circles = []

    sg.stage(title: 'Vanishing Circles', show: true) {
      scene(fill: black, width: 800, height: 600) {
        50.times {
          circles << circle(centerX: rand(800), centerY: rand(600),
              radius: 150, stroke: white, Declarative Stage definition
              strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
            fill rgb(rand(255), rand(255), rand(255), 0.2)
            effect boxBlur(width: 10, height: 10, iterations: 3)
          }
        }
      }
    }
}
                                                                          19
GroovyFX.start { primaryStage ->
  def sg = new SceneGraphBuilder()
  def rand = new Random().&nextInt
  def circles = []
                                Inline property definitions
    sg.stage(title: 'Vanishing Circles', show: true) {
      scene(fill: black, width: 800, height: 600) {
        50.times {
          circles << circle(centerX: rand(800), centerY: rand(600),
              radius: 150, stroke: white,
              strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
            fill rgb(rand(255), rand(255), rand(255), 0.2)
            effect boxBlur(width: 10, height: 10, iterations: 3)
          }
        }
      }
    }
}
                                                                          20
GroovyFX.start { primaryStage ->
  def sg = new SceneGraphBuilder()
  def rand = new Random().&nextInt
  def circles = []

    sg.stage(title: 'Vanishing Circles', show: true) {
      scene(fill: black, width: 800, height: 600) { Bind to properties
        50.times {
          circles << circle(centerX: rand(800), centerY: rand(600),
              radius: 150, stroke: white,
              strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
            fill rgb(rand(255), rand(255), rand(255), 0.2)
            effect boxBlur(width: 10, height: 10, iterations: 3)
          }
        }
      }
    }
}
                                                                          21
GroovyFX.start { primaryStage ->
  def sg = new SceneGraphBuilder()
  def rand = new Random().&nextInt
  def circles = []

    sg.stage(title: 'Vanishing Circles', show: Sequence Creation Via Loop
                                               true) {
      scene(fill: black, width: 800, height: 600) {
        50.times {
          circles << circle(centerX: rand(800), centerY: rand(600),
              radius: 150, stroke: white,
              strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) {
            fill rgb(rand(255), rand(255), rand(255), 0.2)
            effect boxBlur(width: 10, height: 10, iterations: 3)
          }
        }
      }
    }
}
                                                                          22
Animation in GroovyFX
timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) {
  circles.each { circle ->
    at (40.s) {
      change circle.centerXProperty() to rand(800)
      change circle.centerYProperty() to rand(600)
    }
  }
}.play()




                                                                 23
Animation in GroovyFX
timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) {
  circles.each { circle ->
    at (40.s) {
      change circle.centerXProperty() to rand(800)
      change circle.centerYProperty() to rand(600)
    }
  }                                   Easy animation syntax:
}.play()                             at (duration) {keyframes}




                                                                 24
Animation in GroovyFX
timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) {
  circles.each { circle ->
    at (40.s) {
      change circle.centerXProperty() to rand(800)
      change circle.centerYProperty() to rand(600)
    }
  }
}.play()
                                      Key frame DSL



                                                                 25
Animation in GroovyFX
timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) {
  circles.each { circle ->
    at (40.s) {
      change circle.centerXProperty() to rand(800) tween ease_both
      change circle.centerYProperty() to rand(600) tween linear
    }
  }
}.play()
                                            Optional easing




                                                                     26
Event Listeners in GroovyFX
>   Supported using the built-in Closure syntax
>   Optional arguments for event objects

onMouseClicked { e ->
  timeline {
    at(3.s) { change e.source.radiusProperty() to 0 }
  }.play()
}



                                                        27
Event Listeners in GroovyFX
>   Supported using the built-in Closure syntax
>   Optional arguments for event objects

onMouseClicked { MouseEvent e ->
  timeline {
    at(3.s) { change e.source.radiusProperty() to 0 }
  }.play()
}
                       Compact syntax
                          {body}
                                                        28
Event Listeners in GroovyFX
>   Supported using the built-in Closure syntax
>   Optional arguments for event objects          Optional event parameter
                                                       {event -> body}

onMouseClicked { MouseEvent e ->
  timeline {
    at(3.s) { change e.source.radiusProperty() to 0 }
  }.play()
}



                                                                             29
But wait, there is more Grooviness…   30
Properties in Java
public class Person {
 private StringProperty firstName;
 public void setFirstName(String val) { firstNameProperty().set(val); }
 public String getFirstName() { return firstNameProperty().get(); }
 public StringProperty firstNameProperty() {
   if (firstName == null)
     firstName = new SimpleStringProperty(this, "firstName");
   return firstName;
 }

    private StringProperty lastName;
    public void setLastName(String value) { lastNameProperty().set(value); }
    public String getLastName() { return lastNameProperty().get(); }
    public StringProperty lastNameProperty() {
      if (lastName == null) // etc.
    }
}


                                                                               31
Properties in GroovyFX
public class Person {
  @FXBindable String firstName;
  @FXBindable String lastName;
}




                                  32
Properties in GroovyFX
public class Person {
  @FXBindable String firstName;
  @FXBindable String lastName = “Smith”;
}

                                           Optional initializers




                                                                   33
TableView in Java
ObservableList<Person> items = ...
TableView<Person> tableView = new TableView<Person>(items);

TableColumn<Person,String> firstNameCol =
     new TableColumn<Person,String>("First Name");

firstNameCol.setCellValueFactory(
       new Callback<CellDataFeatures<Person, String>,
               ObservableValue<String>>() {
  public ObservableValue<String> call(CellDataFeatures<Person, String> p)
  {
    return p.getValue().firstNameProperty();
  }
});

tableView.getColumns().add(firstNameCol);
                                                                            34
TableView in GroovyFX
def dateFormat = new SimpleDateFormat("yyyy-MM-dd");

tableView(items: persons) {
  tableColumn(property: "name", text: "Name", prefWidth: 150)
  tableColumn(property: "age", text: "Age", prefWidth: 50)
  tableColumn(property: "gender", text: "Gender", prefWidth: 150)
  tableColumn(property: "dob", text: "Birth", prefWidth: 150,
         type: Date,
         converter: { from -> return dateFormat.format(from) })
}




                                                                    35
Layout in Java
TextField urlField = new TextField(“http://www.google.com”);
HBox.setHgrow(urlField, Priority.ALWAYS);

HBox hbox = new HBox();
hbox.getChildren().add(urlField);

WebView webView = new WebView();
VBox.setVgrow(webView, Priority.ALWAYS);

VBox vbox = new VBox();
vbox.getChildren().addAll(hbox, webView);




                                                               36
Layout in GroovyFX

sg.stage(title: "GroovyFX WebView Demo", show: true) {
  scene(fill: groovyblue, width: 1024, height: 800) {
     vbox {
       hbox(padding: 10, spacing: 5) {
          textField(“http://www.yahoo.com”, hgrow: "always")
          button("Go”)
       }
       webView(vgrow: "always")
     }
  }
}

                                                               37
Layout in GroovyFX




                     38
Layout in GroovyFX
gridPane(hgap: 5, vgap: 10, padding: 25) {
 columnConstraints(minWidth: 50, halignment: "right")
 columnConstraints(prefWidth: 250)
 label("Send Us Your Feedback", font: "24pt sanserif",
     row: 0, columnSpan: GridPane.REMAINING, halignment: "center",
     margin: [0, 0, 10])

    label("Name: ", row: 1, column: 0)
    textField(promptText: "Your name", row: 1, column: 1, hgrow: 'always')

    label("Email:", row: 2, column: 0)
    textField(promptText: "Your email", row: 2, column: 1, hgrow: 'always')

    label("Message:", row: 3, column: 0, valignment: "baseline")
    textArea(row: 3, column: 1, hgrow: "always", vgrow: "always")

    button("Send Message", row: 4, column: 1, halignment: "right")
}
                                                                              39
Layout in GroovyFX




                     40
GroovyFX Supports…




                     41
JavaFX With Clojure




        Artwork by Augusto Sellhorn   http://sellmic.com/




                                                            42
A Little About         Clojure
>   Started in 2007 by Rich Hickey
>   Functional Programming Language
>   Derived from LISP
>   Optimized for High Concurrency

                (def hello (fn [] "Hello world"))
                (hello)

>   … and looks nothing like Java!

                                                    43
Clojure Syntax in One Slide
                   Symbols                                               Collections
                                                                         (commas optional)

 >   numbers – 2.178                                 >  Lists
 >   ratios – 355/113                                (1, 2, 3, 4, 5)
 >   strings – “clojure”, “rocks”                    > Vectors

 >   characters – a b c d                        [1, 2, 3, 4, 5]
 >   symbols – a b c d                               > Maps

 >   keywords – :alpha :beta                         {:a 1, :b 2, :c 3, :d 4}
 >   boolean – true, false                           > Sets

 >   null - nil                                      #{:a :b :c :d :e}

             (plus macros that are syntactic sugar wrapping the above)
                                                                                             44
Clojure GUI Example
(defn javafxapp []
  (let [stage (Stage. "JavaFX Stage")
        scene (Scene.)]
    (.setFill scene Color/LIGHTGREEN)
    (.setWidth stage 600)
    (.setHeight stage 450)
    (.setScene stage scene)
    (.setVisible stage true)))
(javafxapp)

                                        45
Refined Clojure GUI Example
(defn javafxapp []
  (doto (Stage. "JavaFX Stage")
    (.setWidth 600)
    (.setHeight 450)
    (.setScene (doto (Scene.)
      (.setFill Color/LIGHTGREEN)
      (.setContent (list (doto (Rectangle.)
        (.setX 25)
        (.setY 40)
        (.setWidth 100)
        (.setHeight 50)
        (.setFill Color/RED))))))
    (.setVisible true)))
(javafxapp)

                                              46
Refined Clojure GUI Example
(defn javafxapp []
  (doto (Stage. "JavaFX Stage")
    (.setWidth 600)                           Doto allows nested data
    (.setHeight 450)
    (.setScene (doto (Scene.)                        structures
      (.setFill Color/LIGHTGREEN)
      (.setContent (list (doto (Rectangle.)
        (.setX 25)
        (.setY 40)
        (.setWidth 100)
        (.setHeight 50)
        (.setFill Color/RED))))))
    (.setVisible true)))
(javafxapp)

                                                                        47
Closures in Clojure
>   Inner classes can be created using proxy




    (.addListener hoverProperty
      (proxy [ChangeListener] []
        (handle [p, o, v]
          (.setFill rect
            (if (.isHover rect) Color/GREEN Color/RED)))))




                                                             48
Closures in Clojure
>   Inner classes can be created using proxy
                                         Proxy form:
                           (proxy [class] [args] fs+)
                           f => (name [params*] body)
    (.addListener hoverProperty
      (proxy [ChangeListener] []
        (handle [p, o, v]
          (.setFill rect
            (if (.isHover rect) Color/GREEN Color/RED)))))




                                                             49
JavaFX With Scala




                    50
What is Scala
     2001                             2006
     • Scala Started                  • Scala v2.0




                       2003/2004                     2011
                       • Scala v1.0                  • Scala 2.9.2 (latest)



>   Started in 2001 by Martin Odersky
>   Compiles to Java bytecodes
>   Pure object-oriented language
>   Also a functional programming language
                                                                              51
Java vs. Scala DSL
public 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);
            new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play();                     }
                                                                                                    }
                                                                                                      }
                                                                                                                 591 Characters
                                                                                                    content = circles

          }                                                                                     }
        });
        circle.setStroke(Color.WHITE);                                                            new Timeline {
        circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty())                     cycleCount = INDEFINITE
          .then(4)                                                                                  autoReverse = true
          .otherwise(0));                                                                           keyFrames = for (circle <- circles) yield at (40 s) {
        circles.add(circle);                                                                          Set(
      }                                                                                                 circle.centerX -> random * stage.width,
      root.getChildren().addAll(circles);                                                               circle.centerY -> random * stage.height
      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();
    }
}




                                                                                                                                                            52
object VanishingCircles extends JFXApp {
  stage = new Stage {
    title = "Disappearing Circles"
    width = 800
    height = 600
    scene = new Scene {
      fill = BLACK
      children = 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)
      }
    }
  }
}
                                                            53
object VanishingCircles extends JFXApp {
  stage = new Stage {
    title = "Disappearing Circles"
    width = 800
    height Base class for JavaFX applications
            = 600
    scene = new Scene {
      fill = BLACK
      children = 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)
      }
    }
  }
}
                                                            54
object VanishingCircles extends JFXApp {
  stage = new Stage {
    title = "Disappearing Circles"
    width = 800
    height = 600
    scene = new Scene {               Declarative Stage definition
      fill = BLACK
      children = 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)
      }
    }
  }
}
                                                                     55
object VanishingCircles extends JFXApp {
  stage = new Stage {
    title = "Disappearing Circles"
    width = 800                          Inline property definitions
    height = 600
    scene = new Scene {
      fill = BLACK
      children = 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)
      }
    }
  }
}
                                                                       56
object VanishingCircles extends JFXApp {
  stage = new Stage {
    title = "Disappearing Circles"
    width = 800
    height = 600                      Sequence Creation Via Loop
    scene = new Scene {
      fill = BLACK
      children = 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)
      }
    }
  }
}
                                                                   57
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"

                                                               58
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();

                                                          59
JavaFX Script-like animation
Animation 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();

                                                            60
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
    )
  }
}
                              Operator overloading for animation
timeline.play();
                                             syntax
                                                                   61
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 tween EASE_BOTH,
 circle.centerY -> random * stage.height tween EASE_IN
    )
  }
}
timeline.play();            Optional tween
                              syntax
                                                           62
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()
    }


                                                  63
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()
    }
                       Compact syntax
                          {body}
                                                  64
Event Listeners in Scala
>   Supported using the built-in Closure syntax
>   Arguments for event objects
                                                  Event parameter
>   100% type-safe                                {(event) => body}


    onMouseClicked = { (e: MouseEvent) =>
      Timeline(at(3 s){radius->0}).play()
    }


                                                                      65
TableView in ScalaFX
def dateFormat = new SimpleDateFormat("yyyy-MM-dd")
new TableView[Speaker](persons) {
  columns = Seq(
   new TableColumn[Speaker, String] {
     text: "Name"
     converter = {_.firstName}
   } new TableColumn[Speaker, String] {
     text: "Age"
     converter = {_.age}
   } new TableColumn[Speaker, String] {
     text: "Gender"
     converter = {_.gender}
   } new TableColumn[Speaker, String] {
     text: "Birth"
     converter = {dateFormat.format(_.dob)},
   }
)}

                                                      66
JavaFX With Visage




                     67
About Project Visage
>   “Visage is a domain specific language (DSL) designed for the
    express purpose of writing user interfaces.”



>   Visage project goals:
       Compile to JavaFX Java APIs
       Evolve the Language (Annotations, Maps, etc.)
       Support Other Toolkits

>   Come join the team!
>   For more info: http://visage-lang.org/
                                                                   68
Java vs. Visage DSL
public class VanishingCircles extends Application {                                           var circles:Circle[];
                                                                                              Stage {
    public static void main(String[] args) {                                                    title: "Vanishing Circles"
      Application.launch(args);                                                                 Scene {
    }                                                                                             width: 800
                                                                                                  height: 600
    @Override                                                                                     fill: BLACK
    public void start(Stage primaryStage) {                                                       Group {
      primaryStage.setTitle("Vanishing Circles");                                                   circles = for (i in [1..50]) {
      Group root = new Group();                                                                       def c:Circle = Circle {
      Scene scene = new Scene(root, 800, 600, Color.BLACK);                                             centerX: random() * 800
      List<Circle> circles = new ArrayList<Circle>();                                                   centerY: random() * 600
      for (int i = 0; i < 50; i++) {                                                                    radius: 150
        final Circle circle = new Circle(150);                                                          fill: color(random(), random(), random(), .2)

                  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));
                                                                                                               35 Lines
                                                                                                        effect: BoxBlur {
                                                                                                          height: 10
                                                                                                          width: 10
                                                                                                          iterations: 3


                  1299 Characters
        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();
                                                                                                        }
                                                                                                               487 Characters
                                                                                                        stroke: WHITE
                                                                                                        strokeWidth: bind if (c.hover) 5 else 0
                                                                                                        onMouseClicked: function(e) {
          }                                                                                               Timeline {at (3s) {c.radius => 0}}.play()
        });                                                                                             }
        circle.setStroke(Color.WHITE);                                                                }
        circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty())                     }
          .then(4)                                                                                }
          .otherwise(0));                                                                       }
        circles.add(circle);                                                                  }
      }
      root.getChildren().addAll(circles);                                                     Timeline {
      primaryStage.setScene(scene);                                                             for (circle in circles) at (40s) {
      primaryStage.show();                                                                        circle.centerX => random() * 800;
                                                                                                  circle.centerY => random() * 600
        Timeline moveCircles = new Timeline();                                                  }
        for (Circle circle : circles) {                                                       }.play()
          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();
    }
}




                                                                                                                                                        69
How about JavaFX on… Visage
Stage {
  title: "Vanishing Circles"
  scene: Scene {
    width: 800
    height: 600
    fill: BLACK
    content: Group {
      circles = for (i in [1..50]) {
        Circle {
          centerX: random() * 800
          centerY: random() * 600
        }
      }
    }
  }
}

                                       70
How about JavaFX on… Visage
Stage {
  title: "Vanishing Circles"
  scene: Scene {
    width: 800
    height: 600
    fill: BLACK
    content: Group {
      circles = for (i in [1..50]) {
        Circle {
          centerX: random() * 800
          centerY: random() * 600
        }
      }
    }
  }
}

                                       71
How about JavaFX on… Visage
Stage {
  title: "Vanishing Circles"
  Scene {
    width: 800
    height: 600
    fill: BLACK
    Group {
      circles = for (i in [1..50]) {
        Circle {
          centerX: random() * 800
          centerY: random() * 600
        }
      }
    }
  }
}

                                       72
Visage is JavaFX Script++
>   Default Parameters
>   New Literal Syntax For:
       Angles – 35deg, 4rad, 1turn
       Colors – #DDCCBB, #AA33AA|CC
       Lengths – 5px, 2pt, 3in, 4sp
>   Null-check Dereference
       var width = rect.!width
>   Built-in Bindable Maps (coming soon!)
       var fruitMap = ["red" : apple, "yellow" : banana]
       var fruit = bind fruitMap["red"]
                                                            73
Visage and JavaFX 2.0 are made for each other…
>   Enhanced Binding
       Retains lazy evaluation properties with additional expressive power
>   Integrated Collections
       Sequences and Maps automatically convert between JavaFX
        Observable Lists/Maps
>   Built-in Animation Syntax
       Ties into JavaFX animation subsystem
       Provides consistent, clean APIs



                                                                              74
Other JVM Languages to Try
>   JRuby
       Faithful to Ruby language with the power of the JVM
>   Gosu
       Up and coming language created at GuideWire
       Easy to enhance libraries and create DSLs
>   Mirah
       Invented by Charles Nutter
       Local Type Inference, Static and Dynamic Typing
>   Fantom
       Created by Brian and Andy Frank
       Portable to Java and .NET
       Local Type Inference, Static and Dynamic Typing

                                                              75
Conclusion
>   You can write JavaFX applications in pure Java
>   JavaFX is also usable in alternate languages
>   You can get improved support using DSL libraries
       GroovyFX
       ScalaFX
>   Or a dedicated UI JVM Language
       Visage
Stephen Chin
                                                            stephen.chin@oracle.com
                                                            tweet: @steveonjava




Thanks to Dean Iverson and Jonathan Giles for help preparing this talk          77

More Related Content

PPTX
JavaFX and Scala - Like Milk and Cookies
PPTX
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
PPT
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
PDF
Mary Had a Little λ (QCon)
PPTX
JavaFX 2.0 With Alternative Languages - JavaOne 2011
PDF
Hacking JavaFX with Groovy, Clojure, Scala, and Visage: Stephen Chin
PPTX
ScalaDays 2014 - Reactive Scala 3D Game Engine
PPTX
Moving from JFreeChart to JavaFX with JavaFX Chart Extensions
JavaFX and Scala - Like Milk and Cookies
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
Mary Had a Little λ (QCon)
JavaFX 2.0 With Alternative Languages - JavaOne 2011
Hacking JavaFX with Groovy, Clojure, Scala, and Visage: Stephen Chin
ScalaDays 2014 - Reactive Scala 3D Game Engine
Moving from JFreeChart to JavaFX with JavaFX Chart Extensions

What's hot (20)

PDF
Java FX 2.0 - A Developer's Guide
PPTX
JavaFX and Scala in the Cloud
PDF
The Ring programming language version 1.6 book - Part 46 of 189
PDF
The Ring programming language version 1.2 book - Part 32 of 84
PDF
Clojure: Functional Concurrency for the JVM (presented at OSCON)
PDF
Beyond Breakpoints: Advanced Debugging with XCode
PPT
SDC - Einführung in Scala
PDF
Backbone.js: Run your Application Inside The Browser
PDF
Zend Framework 1 + Doctrine 2
PDF
Scala in practice
PDF
The Ring programming language version 1.2 book - Part 79 of 84
PDF
The Ring programming language version 1.9 book - Part 53 of 210
PDF
Alternate JVM Languages
PPTX
Php forum2015 tomas_final
PDF
The Ring programming language version 1.2 book - Part 35 of 84
PDF
Clojure Deep Dive
PDF
Scala vs Java 8 in a Java 8 World
PPTX
On the Impossibility of Batch Update for Cryptographic Accumulators
PDF
PHP and MySQL Tips and tricks, DC 2007
PPT
Xm lparsers
Java FX 2.0 - A Developer's Guide
JavaFX and Scala in the Cloud
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.2 book - Part 32 of 84
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Beyond Breakpoints: Advanced Debugging with XCode
SDC - Einführung in Scala
Backbone.js: Run your Application Inside The Browser
Zend Framework 1 + Doctrine 2
Scala in practice
The Ring programming language version 1.2 book - Part 79 of 84
The Ring programming language version 1.9 book - Part 53 of 210
Alternate JVM Languages
Php forum2015 tomas_final
The Ring programming language version 1.2 book - Part 35 of 84
Clojure Deep Dive
Scala vs Java 8 in a Java 8 World
On the Impossibility of Batch Update for Cryptographic Accumulators
PHP and MySQL Tips and tricks, DC 2007
Xm lparsers

Viewers also liked (20)

PDF
DukeScript
PPTX
Internet of Things Magic Show
PPTX
Confessions of a Former Agile Methodologist
PPTX
Java on Raspberry Pi Lab
PPTX
JCrete Embedded Java Workshop
PPTX
Raspberry Pi with Java (JJUG)
PPTX
Raspberry Pi à la GroovyFX
PDF
JavaFX GUI architecture with Clojure core.async
PPTX
Zombie Time - JSR 310 for the Undead
PDF
Raspberry Pi Gaming 4 Kids - Dutch Version
PPTX
Raspberry pi gaming 4 kids
PPTX
RetroPi Handheld Raspberry Pi Gaming Console
PPTX
OpenJFX on Android and Devices
PPTX
JavaFX on Mobile (by Johan Vos)
PPTX
Raspberry Pi Gaming 4 Kids (Devoxx4Kids)
PPTX
Confessions of a Former Agile Methodologist (JFrog Edition)
PPTX
Oracle IoT Kids Workshop
PDF
Java 8 for Tablets, Pis, and Legos
PPTX
Devoxx4Kids NAO Workshop
PPTX
Devoxx4Kids Lego Workshop
DukeScript
Internet of Things Magic Show
Confessions of a Former Agile Methodologist
Java on Raspberry Pi Lab
JCrete Embedded Java Workshop
Raspberry Pi with Java (JJUG)
Raspberry Pi à la GroovyFX
JavaFX GUI architecture with Clojure core.async
Zombie Time - JSR 310 for the Undead
Raspberry Pi Gaming 4 Kids - Dutch Version
Raspberry pi gaming 4 kids
RetroPi Handheld Raspberry Pi Gaming Console
OpenJFX on Android and Devices
JavaFX on Mobile (by Johan Vos)
Raspberry Pi Gaming 4 Kids (Devoxx4Kids)
Confessions of a Former Agile Methodologist (JFrog Edition)
Oracle IoT Kids Workshop
Java 8 for Tablets, Pis, and Legos
Devoxx4Kids NAO Workshop
Devoxx4Kids Lego Workshop

Similar to Hacking JavaFX with Groovy, Clojure, Scala, and Visage (20)

PPTX
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
PPTX
JavaFX 2.0 With Alternative Languages [Portuguese]
PPTX
JavaFX Your Way - Devoxx Version
PDF
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
PDF
Don't panic in Fortaleza - ScalaFX
PPTX
JavaFX 2.0 and Alternative Languages
PDF
Painless Persistence with Realm
PDF
Greach, GroovyFx Workshop
PDF
Scala @ TechMeetup Edinburgh
PPTX
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
PDF
JJUG CCC 2011 Spring
PDF
Coding in Style
PDF
Scala introduction
PPTX
ES6(ES2015) is beautiful
PDF
Building mobile web apps with Mobello
PDF
Introduction to Scala
PDF
SVGo workshop
PDF
TypeScript Introduction
PPTX
ES6 Overview
PDF
Emerging Languages: A Tour of the Horizon
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
JavaFX 2.0 With Alternative Languages [Portuguese]
JavaFX Your Way - Devoxx Version
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
Don't panic in Fortaleza - ScalaFX
JavaFX 2.0 and Alternative Languages
Painless Persistence with Realm
Greach, GroovyFx Workshop
Scala @ TechMeetup Edinburgh
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
JJUG CCC 2011 Spring
Coding in Style
Scala introduction
ES6(ES2015) is beautiful
Building mobile web apps with Mobello
Introduction to Scala
SVGo workshop
TypeScript Introduction
ES6 Overview
Emerging Languages: A Tour of the Horizon

More from Stephen Chin (9)

PPTX
DevOps Tools for Java Developers v2
PPTX
10 Ways Everyone Can Support the Java Community
PPTX
Java Clients and JavaFX: The Definitive Guide
PPTX
DevOps Tools for Java Developers
PPTX
Java Clients and JavaFX - Presented to LJC
PPTX
LUGOD Raspberry Pi Hacking
PPTX
Moving to the Client - JavaFX and HTML5
PPTX
JavaFX 2 - A Java Developer's Guide (San Antonio JUG Version)
PPTX
JavaFX 2 Using the Spring Framework
DevOps Tools for Java Developers v2
10 Ways Everyone Can Support the Java Community
Java Clients and JavaFX: The Definitive Guide
DevOps Tools for Java Developers
Java Clients and JavaFX - Presented to LJC
LUGOD Raspberry Pi Hacking
Moving to the Client - JavaFX and HTML5
JavaFX 2 - A Java Developer's Guide (San Antonio JUG Version)
JavaFX 2 Using the Spring Framework

Recently uploaded (20)

PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Machine learning based COVID-19 study performance prediction
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Electronic commerce courselecture one. Pdf
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
A Presentation on Artificial Intelligence
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Approach and Philosophy of On baking technology
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Chapter 3 Spatial Domain Image Processing.pdf
NewMind AI Monthly Chronicles - July 2025
Machine learning based COVID-19 study performance prediction
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Diabetes mellitus diagnosis method based random forest with bat algorithm
Advanced methodologies resolving dimensionality complications for autism neur...
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
“AI and Expert System Decision Support & Business Intelligence Systems”
Spectral efficient network and resource selection model in 5G networks
Electronic commerce courselecture one. Pdf
Encapsulation_ Review paper, used for researhc scholars
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Dropbox Q2 2025 Financial Results & Investor Presentation
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
A Presentation on Artificial Intelligence
Unlocking AI with Model Context Protocol (MCP)
MYSQL Presentation for SQL database connectivity
Approach and Philosophy of On baking technology
Bridging biosciences and deep learning for revolutionary discoveries: a compr...

Hacking JavaFX with Groovy, Clojure, Scala, and Visage

  • 1. Hacking JavaFX with Groovy, Clojure, Scala, and Visage Stephen Chin Java Evangelist, Oracle stephen.chin@oracle.com tweet: @steveonjava
  • 2. Meet the Presenter Stephen Chin > Java Evangelist, Oracle > Author, Pro JavaFX Platform 2 Family Man > Open Source Hacker  JFXtras  ScalaFX Motorcyclist  Visage > User Group Co-Leader  Silicon Valley JavaFX User Group  Streamed Live!
  • 3. Safe Harbor Statement The following is intended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle.
  • 4. JavaFX 2.0 Platform Immersive Application Experience Leverage your Java skills with modern JavaFX APIs > 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.
  • 6. Programming Languages > JavaFX 2.0 APIs are now in Java  Pure Java APIs for all of JavaFX  Binding and Sequences exposed as Java APIs  FXML Markup for tooling > Embrace all JVM languages  Groovy, Scala, Clojure, JRuby  Fantom, Mira, Gosu, Jython, etc. > JavaFX Script is no longer supported by Oracle  Existing JavaFX Script based applications will continue to run  Visage is the open-source successor to the JavaFX Script language
  • 8. Vanishing Circles in Java 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); 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)); 40 Lines 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); 1299 Characters 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(); } } 8
  • 9. 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…] } }
  • 10. 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); } 10
  • 11. Setup Binding circle.strokeWidthProperty().bind(Bindings .when(circle.hoverProperty()) .then(4) .otherwise(0) ); 11
  • 12. 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(); } }); 12
  • 13. 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(); 13
  • 15. Features of Groovy > Modern language  Closures  AST Transforms  Strongly typed dynamic language > Tight integration with Java  Very easy to port from Java to Groovy > Declarative syntax with GroovyFX Builders  Familiar to Groovy and JavaFX Script developers
  • 16. Java vs. GroovyFX DSL public class VanishingCircles extends Application { GroovyFX.start { primaryStage -> def sg = new SceneGraphBuilder() public static void main(String[] args) { def rand = new Random().&nextInt Application.launch(args); def circles = [] } sg.stage(title: 'Vanishing Circles', show: true) { @Override scene(fill: black, width: 800, height: 600) { public void start(Stage primaryStage) { 50.times { primaryStage.setTitle("Vanishing Circles"); circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white, Group root = new Group(); strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) { Scene scene = new Scene(root, 800, 600, Color.BLACK); fill rgb(rand(255), rand(255), rand(255), 0.2) List<Circle> circles = new ArrayList<Circle>(); effect boxBlur(width: 10, height: 10, iterations: 3) for (int i = 0; i < 50; i++) { onMouseClicked { e -> 29 Lines final Circle circle = new Circle(150); timeline { 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)); } } at(3.s) { change e.source.radiusProperty() to 0 } }.play() 1299 Characters 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(); } } 671 Characters timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) { } circles.each { circle -> }); at (40.s) { circle.setStroke(Color.WHITE); change circle.centerXProperty() to rand(800) circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) change circle.centerYProperty() to rand(600) .then(4) } .otherwise(0)); } circles.add(circle); }.play() } } 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(); } } 16
  • 17. GroovyFX.start { primaryStage -> def sg = new SceneGraphBuilder() def rand = new Random().&nextInt def circles = [] sg.stage(title: 'Vanishing Circles', show: true) { scene(fill: black, width: 800, height: 600) { 50.times { circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white, strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) { fill rgb(rand(255), rand(255), rand(255), 0.2) effect boxBlur(width: 10, height: 10, iterations: 3) } } } } } 17
  • 18. GroovyFX.start { primaryStage -> def sg = new SceneGraphBuilder() def rand = new Random().&nextInt def circles = [] Builder for GroovyFX scene graphs sg.stage(title: 'Vanishing Circles', show: true) { scene(fill: black, width: 800, height: 600) { 50.times { circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white, strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) { fill rgb(rand(255), rand(255), rand(255), 0.2) effect boxBlur(width: 10, height: 10, iterations: 3) } } } } } 18
  • 19. GroovyFX.start { primaryStage -> def sg = new SceneGraphBuilder() def rand = new Random().&nextInt def circles = [] sg.stage(title: 'Vanishing Circles', show: true) { scene(fill: black, width: 800, height: 600) { 50.times { circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white, Declarative Stage definition strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) { fill rgb(rand(255), rand(255), rand(255), 0.2) effect boxBlur(width: 10, height: 10, iterations: 3) } } } } } 19
  • 20. GroovyFX.start { primaryStage -> def sg = new SceneGraphBuilder() def rand = new Random().&nextInt def circles = [] Inline property definitions sg.stage(title: 'Vanishing Circles', show: true) { scene(fill: black, width: 800, height: 600) { 50.times { circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white, strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) { fill rgb(rand(255), rand(255), rand(255), 0.2) effect boxBlur(width: 10, height: 10, iterations: 3) } } } } } 20
  • 21. GroovyFX.start { primaryStage -> def sg = new SceneGraphBuilder() def rand = new Random().&nextInt def circles = [] sg.stage(title: 'Vanishing Circles', show: true) { scene(fill: black, width: 800, height: 600) { Bind to properties 50.times { circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white, strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) { fill rgb(rand(255), rand(255), rand(255), 0.2) effect boxBlur(width: 10, height: 10, iterations: 3) } } } } } 21
  • 22. GroovyFX.start { primaryStage -> def sg = new SceneGraphBuilder() def rand = new Random().&nextInt def circles = [] sg.stage(title: 'Vanishing Circles', show: Sequence Creation Via Loop true) { scene(fill: black, width: 800, height: 600) { 50.times { circles << circle(centerX: rand(800), centerY: rand(600), radius: 150, stroke: white, strokeWidth: bind('hover', converter: {val -> val ? 4 : 0})) { fill rgb(rand(255), rand(255), rand(255), 0.2) effect boxBlur(width: 10, height: 10, iterations: 3) } } } } } 22
  • 23. Animation in GroovyFX timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) { circles.each { circle -> at (40.s) { change circle.centerXProperty() to rand(800) change circle.centerYProperty() to rand(600) } } }.play() 23
  • 24. Animation in GroovyFX timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) { circles.each { circle -> at (40.s) { change circle.centerXProperty() to rand(800) change circle.centerYProperty() to rand(600) } } Easy animation syntax: }.play() at (duration) {keyframes} 24
  • 25. Animation in GroovyFX timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) { circles.each { circle -> at (40.s) { change circle.centerXProperty() to rand(800) change circle.centerYProperty() to rand(600) } } }.play() Key frame DSL 25
  • 26. Animation in GroovyFX timeline(cycleCount: Timeline.INDEFINITE, autoReverse: true) { circles.each { circle -> at (40.s) { change circle.centerXProperty() to rand(800) tween ease_both change circle.centerYProperty() to rand(600) tween linear } } }.play() Optional easing 26
  • 27. Event Listeners in GroovyFX > Supported using the built-in Closure syntax > Optional arguments for event objects onMouseClicked { e -> timeline { at(3.s) { change e.source.radiusProperty() to 0 } }.play() } 27
  • 28. Event Listeners in GroovyFX > Supported using the built-in Closure syntax > Optional arguments for event objects onMouseClicked { MouseEvent e -> timeline { at(3.s) { change e.source.radiusProperty() to 0 } }.play() } Compact syntax {body} 28
  • 29. Event Listeners in GroovyFX > Supported using the built-in Closure syntax > Optional arguments for event objects Optional event parameter {event -> body} onMouseClicked { MouseEvent e -> timeline { at(3.s) { change e.source.radiusProperty() to 0 } }.play() } 29
  • 30. But wait, there is more Grooviness… 30
  • 31. Properties in Java public class Person { private StringProperty firstName; public void setFirstName(String val) { firstNameProperty().set(val); } public String getFirstName() { return firstNameProperty().get(); } public StringProperty firstNameProperty() { if (firstName == null) firstName = new SimpleStringProperty(this, "firstName"); return firstName; } private StringProperty lastName; public void setLastName(String value) { lastNameProperty().set(value); } public String getLastName() { return lastNameProperty().get(); } public StringProperty lastNameProperty() { if (lastName == null) // etc. } } 31
  • 32. Properties in GroovyFX public class Person { @FXBindable String firstName; @FXBindable String lastName; } 32
  • 33. Properties in GroovyFX public class Person { @FXBindable String firstName; @FXBindable String lastName = “Smith”; } Optional initializers 33
  • 34. TableView in Java ObservableList<Person> items = ... TableView<Person> tableView = new TableView<Person>(items); TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name"); firstNameCol.setCellValueFactory( new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() { public ObservableValue<String> call(CellDataFeatures<Person, String> p) { return p.getValue().firstNameProperty(); } }); tableView.getColumns().add(firstNameCol); 34
  • 35. TableView in GroovyFX def dateFormat = new SimpleDateFormat("yyyy-MM-dd"); tableView(items: persons) { tableColumn(property: "name", text: "Name", prefWidth: 150) tableColumn(property: "age", text: "Age", prefWidth: 50) tableColumn(property: "gender", text: "Gender", prefWidth: 150) tableColumn(property: "dob", text: "Birth", prefWidth: 150, type: Date, converter: { from -> return dateFormat.format(from) }) } 35
  • 36. Layout in Java TextField urlField = new TextField(“http://www.google.com”); HBox.setHgrow(urlField, Priority.ALWAYS); HBox hbox = new HBox(); hbox.getChildren().add(urlField); WebView webView = new WebView(); VBox.setVgrow(webView, Priority.ALWAYS); VBox vbox = new VBox(); vbox.getChildren().addAll(hbox, webView); 36
  • 37. Layout in GroovyFX sg.stage(title: "GroovyFX WebView Demo", show: true) { scene(fill: groovyblue, width: 1024, height: 800) { vbox { hbox(padding: 10, spacing: 5) { textField(“http://www.yahoo.com”, hgrow: "always") button("Go”) } webView(vgrow: "always") } } } 37
  • 39. Layout in GroovyFX gridPane(hgap: 5, vgap: 10, padding: 25) { columnConstraints(minWidth: 50, halignment: "right") columnConstraints(prefWidth: 250) label("Send Us Your Feedback", font: "24pt sanserif", row: 0, columnSpan: GridPane.REMAINING, halignment: "center", margin: [0, 0, 10]) label("Name: ", row: 1, column: 0) textField(promptText: "Your name", row: 1, column: 1, hgrow: 'always') label("Email:", row: 2, column: 0) textField(promptText: "Your email", row: 2, column: 1, hgrow: 'always') label("Message:", row: 3, column: 0, valignment: "baseline") textArea(row: 3, column: 1, hgrow: "always", vgrow: "always") button("Send Message", row: 4, column: 1, halignment: "right") } 39
  • 42. JavaFX With Clojure Artwork by Augusto Sellhorn http://sellmic.com/ 42
  • 43. A Little About Clojure > Started in 2007 by Rich Hickey > Functional Programming Language > Derived from LISP > Optimized for High Concurrency (def hello (fn [] "Hello world")) (hello) > … and looks nothing like Java! 43
  • 44. Clojure Syntax in One Slide Symbols Collections (commas optional) > numbers – 2.178 > Lists > ratios – 355/113 (1, 2, 3, 4, 5) > strings – “clojure”, “rocks” > Vectors > characters – a b c d [1, 2, 3, 4, 5] > symbols – a b c d > Maps > keywords – :alpha :beta {:a 1, :b 2, :c 3, :d 4} > boolean – true, false > Sets > null - nil #{:a :b :c :d :e} (plus macros that are syntactic sugar wrapping the above) 44
  • 45. Clojure GUI Example (defn javafxapp [] (let [stage (Stage. "JavaFX Stage") scene (Scene.)] (.setFill scene Color/LIGHTGREEN) (.setWidth stage 600) (.setHeight stage 450) (.setScene stage scene) (.setVisible stage true))) (javafxapp) 45
  • 46. Refined Clojure GUI Example (defn javafxapp [] (doto (Stage. "JavaFX Stage") (.setWidth 600) (.setHeight 450) (.setScene (doto (Scene.) (.setFill Color/LIGHTGREEN) (.setContent (list (doto (Rectangle.) (.setX 25) (.setY 40) (.setWidth 100) (.setHeight 50) (.setFill Color/RED)))))) (.setVisible true))) (javafxapp) 46
  • 47. Refined Clojure GUI Example (defn javafxapp [] (doto (Stage. "JavaFX Stage") (.setWidth 600) Doto allows nested data (.setHeight 450) (.setScene (doto (Scene.) structures (.setFill Color/LIGHTGREEN) (.setContent (list (doto (Rectangle.) (.setX 25) (.setY 40) (.setWidth 100) (.setHeight 50) (.setFill Color/RED)))))) (.setVisible true))) (javafxapp) 47
  • 48. Closures in Clojure > Inner classes can be created using proxy (.addListener hoverProperty (proxy [ChangeListener] [] (handle [p, o, v] (.setFill rect (if (.isHover rect) Color/GREEN Color/RED))))) 48
  • 49. Closures in Clojure > Inner classes can be created using proxy Proxy form: (proxy [class] [args] fs+) f => (name [params*] body) (.addListener hoverProperty (proxy [ChangeListener] [] (handle [p, o, v] (.setFill rect (if (.isHover rect) Color/GREEN Color/RED))))) 49
  • 51. What is Scala 2001 2006 • Scala Started • Scala v2.0 2003/2004 2011 • Scala v1.0 • Scala 2.9.2 (latest) > Started in 2001 by Martin Odersky > Compiles to Java bytecodes > Pure object-oriented language > Also a functional programming language 51
  • 52. Java vs. Scala DSL public 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); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); } } } 591 Characters content = circles } } }); circle.setStroke(Color.WHITE); new Timeline { circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) cycleCount = INDEFINITE .then(4) autoReverse = true .otherwise(0)); keyFrames = for (circle <- circles) yield at (40 s) { circles.add(circle); Set( } circle.centerX -> random * stage.width, root.getChildren().addAll(circles); circle.centerY -> random * stage.height 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(); } } 52
  • 53. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK children = 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) } } } } 53
  • 54. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height Base class for JavaFX applications = 600 scene = new Scene { fill = BLACK children = 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) } } } } 54
  • 55. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { Declarative Stage definition fill = BLACK children = 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) } } } } 55
  • 56. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 Inline property definitions height = 600 scene = new Scene { fill = BLACK children = 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) } } } } 56
  • 57. object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 Sequence Creation Via Loop scene = new Scene { fill = BLACK children = 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) } } } } 57
  • 58. 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" 58
  • 59. 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(); 59
  • 60. JavaFX Script-like animation Animation 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(); 60
  • 61. 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 ) } } Operator overloading for animation timeline.play(); syntax 61
  • 62. 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 tween EASE_BOTH, circle.centerY -> random * stage.height tween EASE_IN ) } } timeline.play(); Optional tween syntax 62
  • 63. 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() } 63
  • 64. 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() } Compact syntax {body} 64
  • 65. Event Listeners in Scala > Supported using the built-in Closure syntax > Arguments for event objects Event parameter > 100% type-safe {(event) => body} onMouseClicked = { (e: MouseEvent) => Timeline(at(3 s){radius->0}).play() } 65
  • 66. TableView in ScalaFX def dateFormat = new SimpleDateFormat("yyyy-MM-dd") new TableView[Speaker](persons) { columns = Seq( new TableColumn[Speaker, String] { text: "Name" converter = {_.firstName} } new TableColumn[Speaker, String] { text: "Age" converter = {_.age} } new TableColumn[Speaker, String] { text: "Gender" converter = {_.gender} } new TableColumn[Speaker, String] { text: "Birth" converter = {dateFormat.format(_.dob)}, } )} 66
  • 68. About Project Visage > “Visage is a domain specific language (DSL) designed for the express purpose of writing user interfaces.” > Visage project goals:  Compile to JavaFX Java APIs  Evolve the Language (Annotations, Maps, etc.)  Support Other Toolkits > Come join the team! > For more info: http://visage-lang.org/ 68
  • 69. Java vs. Visage DSL public class VanishingCircles extends Application { var circles:Circle[]; Stage { public static void main(String[] args) { title: "Vanishing Circles" Application.launch(args); Scene { } width: 800 height: 600 @Override fill: BLACK public void start(Stage primaryStage) { Group { primaryStage.setTitle("Vanishing Circles"); circles = for (i in [1..50]) { Group root = new Group(); def c:Circle = Circle { Scene scene = new Scene(root, 800, 600, Color.BLACK); centerX: random() * 800 List<Circle> circles = new ArrayList<Circle>(); centerY: random() * 600 for (int i = 0; i < 50; i++) { radius: 150 final Circle circle = new Circle(150); fill: color(random(), random(), random(), .2) 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)); 35 Lines effect: BoxBlur { height: 10 width: 10 iterations: 3 1299 Characters 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(); } 487 Characters stroke: WHITE strokeWidth: bind if (c.hover) 5 else 0 onMouseClicked: function(e) { } Timeline {at (3s) {c.radius => 0}}.play() }); } circle.setStroke(Color.WHITE); } circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) } .then(4) } .otherwise(0)); } circles.add(circle); } } root.getChildren().addAll(circles); Timeline { primaryStage.setScene(scene); for (circle in circles) at (40s) { primaryStage.show(); circle.centerX => random() * 800; circle.centerY => random() * 600 Timeline moveCircles = new Timeline(); } for (Circle circle : circles) { }.play() 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(); } } 69
  • 70. How about JavaFX on… Visage Stage { title: "Vanishing Circles" scene: Scene { width: 800 height: 600 fill: BLACK content: Group { circles = for (i in [1..50]) { Circle { centerX: random() * 800 centerY: random() * 600 } } } } } 70
  • 71. How about JavaFX on… Visage Stage { title: "Vanishing Circles" scene: Scene { width: 800 height: 600 fill: BLACK content: Group { circles = for (i in [1..50]) { Circle { centerX: random() * 800 centerY: random() * 600 } } } } } 71
  • 72. How about JavaFX on… Visage Stage { title: "Vanishing Circles" Scene { width: 800 height: 600 fill: BLACK Group { circles = for (i in [1..50]) { Circle { centerX: random() * 800 centerY: random() * 600 } } } } } 72
  • 73. Visage is JavaFX Script++ > Default Parameters > New Literal Syntax For:  Angles – 35deg, 4rad, 1turn  Colors – #DDCCBB, #AA33AA|CC  Lengths – 5px, 2pt, 3in, 4sp > Null-check Dereference  var width = rect.!width > Built-in Bindable Maps (coming soon!)  var fruitMap = ["red" : apple, "yellow" : banana]  var fruit = bind fruitMap["red"] 73
  • 74. Visage and JavaFX 2.0 are made for each other… > Enhanced Binding  Retains lazy evaluation properties with additional expressive power > Integrated Collections  Sequences and Maps automatically convert between JavaFX Observable Lists/Maps > Built-in Animation Syntax  Ties into JavaFX animation subsystem  Provides consistent, clean APIs 74
  • 75. Other JVM Languages to Try > JRuby  Faithful to Ruby language with the power of the JVM > Gosu  Up and coming language created at GuideWire  Easy to enhance libraries and create DSLs > Mirah  Invented by Charles Nutter  Local Type Inference, Static and Dynamic Typing > Fantom  Created by Brian and Andy Frank  Portable to Java and .NET  Local Type Inference, Static and Dynamic Typing 75
  • 76. Conclusion > You can write JavaFX applications in pure Java > JavaFX is also usable in alternate languages > You can get improved support using DSL libraries  GroovyFX  ScalaFX > Or a dedicated UI JVM Language  Visage
  • 77. Stephen Chin stephen.chin@oracle.com tweet: @steveonjava Thanks to Dean Iverson and Jonathan Giles for help preparing this talk 77

Editor's Notes