Movatterモバイル変換


[0]ホーム

URL:


Uploaded byjaxconf
676 views

Hacking JavaFX with Groovy, Clojure, Scala, and Visage: Stephen Chin

The document presents an overview of JavaFX 2.0, highlighting its open-source status and capabilities for building immersive applications using modern Java APIs, supporting multiple JVM languages, including Groovy, Scala, and Clojure. It describes key features such as binding, observable properties, and examples of animations and UI components implemented in Java and GroovyFX. The presenter, Stephen Chin, is an advocate for utilizing Java skills in the development of cross-platform applications with a focus on rich graphics and interactions.

Embed presentation

Downloaded 64 times
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                                          l    JFXtras                                          l    ScalaFX                      Motorcyclist        l    Visage                                     >    User Group Co-Leader                                          l    Silicon Valley JavaFX                                                User Group                                          l    Streamed Live!
Disclaimer:This is Code-Heavy
JavaFX 2.0 PlatformImmersive Application ExperienceLeverage your Java skills with modern JavaFXAPIs>    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 is Now Open Source!Part of the OpenJDK ProjectControls available now, additional codeadded incrementallyProject Page:>  http://openjdk.java.net/projects/openjfx/                                               5
And Will Run on Tablets!*>  iPad (iOS)>  Linux (Popular     Platform for     Tablets That     Runs Something     Similar to Java) *No Release Timeline Announced Yet                                      6
JavaFX With Java
Programming Languages>    JavaFX 2.0 APIs are now in Java     l    Pure Java APIs for all of JavaFX     l    Binding and Sequences exposed as Java APIs     l    FXML Markup for tooling>    Embrace all JVM languages     l    Groovy, Scala, Clojure, JRuby     l    Fantom, Mira, Gosu, Jython, etc.>    JavaFX Script is no longer supported by Oracle     l    Existing JavaFX Script based applications will continue to run     l    Visage is the open-source successor to the JavaFX Script language
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
Example Applicationpublic  class  HelloStage  extends  Application  {        @Override  public  void  start(Stage  stage)  {          stage.setTitle("Hello  Stage");          stage.setWidth(600);          stage.setHeight(450);            Group  root  =  new  Group();          Scene  scene  =  new  Scene(root);          scene.setFill(Color.LIGHTGREEN);            stage.setScene(scene);          stage.show();      }        public  static  void  main(String[]  args)  {          Application.launch(args);      }  }  
Example Application Using Builderspublic  class  HelloStage  extends  Application  {        @Override  public  void  start(Stage  stage)  {          stage.setTitle("Hello  Stage");          stage.setScene(SceneBuilder.create()              .fill(Color.LIGHTGREEN)              .width(600)              .height(450)          .build());          stage.show();      }        public  static  void  main(String[]  args)  {          Application.launch(args);      }  }  
Observable Properties>  Supports watching for changes to properties>  Implemented via anonymous inner classes>  Will take advantage of closures in the future
Observable Pseudo-Properties  final  Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);  rect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {                  });  
Observable Pseudo-Properties  final  Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);                                                             The property      we want to watchrect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {                });  
Observable Pseudo-Properties  final  Rectangle  rect  =  new  Rectangle();  rect.setX(40);                                           Only one listener used with generics   torect.setY(40);                                                   specify the data typerect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {                    });  
Observable Pseudo-Properties  final  Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);  rect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {      public  void  changed(ObservableValue<?  extends  Boolean>  property,  Boolean  oldValue,  Boolean  value)  {        }  });                              Refers to the                      Rectangle.hoverProperty()
Observable Pseudo-Properties  final  Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);  rect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {      public  void  changed(ObservableValue<?  extends  Boolean>  property,  Boolean  oldValue,  Boolean  value)  {          rect.setFill(rect.isHover()  ?  Color.GREEN  :  Color.RED);      }  });  
Binding>  Unquestionably the biggest JavaFX Script innovation>  Supported via a PropertyBinding class>  Lazy invocation for high performance>  Static construction syntax for simple cases     l    e.g.: bind(<property>), bindBiDirectional(<property>)
Sequences in Java>    Replaced with an Observable List>    Public API is based on JavaFX sequences>    Internal code can use lighter collections API>    JavaFX 2.0 also has an Observable Map
Vanishing Circles                    !                        20
Vanishing Circles in Javapublic  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++)  {                                                                                                                                   40 Lines            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));                                                                                                                                   1299 Characters            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();      }  }                                                                                                                                                     21
Application Skeletonpublic  class  VanishingCircles  extends  Application  {      public  static  void  main(String[]  args)  {          Application.launch(args);      }      @Override      public  void  start(Stage  primaryStage)  {          primaryStage.setTitle("Vanishing  Circles");          Group  root  =  new  Group();          Scene  scene  =  new  Scene(root,  800,  600,  Color.BLACK);          [create  the  circles…]          root.getChildren().addAll(circles);          primaryStage.setScene(scene);          primaryStage.show();          [begin  the  animation…]      }  }  
Create the CirclesList<Circle>  circles  =  new  ArrayList<Circle>();  for  (int  i  =  0;  i  <  50;  i++)  {      final  Circle  circle  =  new  Circle(150);      circle.setCenterX(Math.random()  *  800);      circle.setCenterY(Math.random()  *  600);      circle.setFill(new  Color(Math.random(),  Math.random(),                                                        Math.random(),  .2));      circle.setEffect(new  BoxBlur(10,  10,  3));      circle.setStroke(Color.WHITE);      [setup  binding…]      [setup  event  listeners…]      circles.add(circle);  }                                                                                                                                            23
Setup Bindingcircle.strokeWidthProperty().bind(Bindings      .when(circle.hoverProperty())      .then(4)      .otherwise(0)  );                                                   24
Setup Event Listenerscircle.addEventHandler(MouseEvent.MOUSE_CLICKED,                                                  new  EventHandler<MouseEvent>()  {      public  void  handle(MouseEvent  t)  {          KeyValue  collapse  =  new  KeyValue(circle.radiusProperty(),  0);          new  Timeline(new  KeyFrame(Duration.seconds(3),                                                                collapse)).play();      }  });                                                                                                                                                   25
Begin the AnimationTimeline  moveCircles  =  new  Timeline();  for  (Circle  circle  :  circles)  {      KeyValue  moveX  =  new  KeyValue(circle.centerXProperty(),                                                                    Math.random()  *  800);      KeyValue  moveY  =  new  KeyValue(circle.centerYProperty(),                                                                    Math.random()  *  600);      moveCircles.getKeyFrames().add(new  KeyFrame(Duration.seconds(40),                                                                                                                                                                                                                                 moveX,  moveY));  }  moveCircles.play();                                                                                                                                                                                                                 26
JavaFX With Groovy
Features of Groovy>    Modern language     l    Closures     l    AST Transforms     l    Strongly typed dynamic language>    Tight integration with Java     l    Very easy to port from Java to Groovy>    Declarative syntax with GroovyFX Builders     l    Familiar to Groovy and JavaFX Script developers
Java vs. GroovyFX DSLpublic  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  -­‐>                       40 Lines                                                                                                                                      29 Lines            final  Circle  circle  =  new  Circle(150);                                                                          timeline  {              circle.setCenterX(Math.random()  *  800);                                                                                      at(3.s)  {  change  e.source.radiusProperty()  to  0  }              circle.setCenterY(Math.random()  *  600);                                                                                  }.play()              circle.setFill(new  Color(Math.random(),  Math.random(),  Math.random(),  .2));                                    }                                                                                                                                                                     671 Characters            circle.setEffect(new  BoxBlur(10,  10,  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();                                                                                                                                               }                                                                                                                                           }                                                                                                                                                                                                                                                                              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();      }  }                                                                                                                                                                                                                                                                                                        29
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)                  }              }          }      }  }                                                                                                                                               30
GroovyFX.start  {  primaryStage  -­‐>      def  sg  =  new  SceneGraphBuilder()      def  rand  =  new  Random().&nextInt      def  circles  =  []        sg.stage(title:  'Vanishing  Circles',  show:  true)  {       Builder for GroovyFX scene graphs        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)                  }              }          }      }  }                                                                                                                                               31
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)                  }              }          }      }  }                                                                                                                                               32
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)                  }              }          }      }  }                                                                                                                                               33
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)                  }              }          }      }  }                                                                                                                                               34
GroovyFX.start  {  primaryStage  -­‐>      def  sg  =  new  SceneGraphBuilder()      def  rand  =  new  Random().&nextInt      def  circles  =  []        sg.stage(title:  'Vanishing  Circles',  show:  true)  {   Creation Via Loop                                                                                         Sequence        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)                  }              }          }      }  }                                                                                                                                               35
Animation in GroovyFXtimeline(cycleCount:  Timeline.INDEFINITE,  autoReverse:  true)  {      circles.each  {  circle  -­‐>          at  (40.s)  {              change  circle.centerXProperty()  to  rand(800)              change  circle.centerYProperty()  to  rand(600)          }      }  }.play()                                                                                      36
Animation in GroovyFXtimeline(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}                                                                                      37
Animation in GroovyFXtimeline(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                                                                                    38
Animation in GroovyFXtimeline(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                                                                                                            39
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()   }                                                                                             40
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}                                                                                           41
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()   }                                                                                             42
But wait, there is more Grooviness…   43
Properties in Javapublic 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.!       } !}!                                                                                     44
Properties in GroovyFXpublic class Person {!     @FXBindable String firstName; !     @FXBindable String lastName;!}!                                         45
Properties in GroovyFXpublic class Person {!     @FXBindable String firstName; !     @FXBindable String lastName = “Smith”;!}!                                             Optional initializers                                                                   46
Properties in GroovyFXpublic class Person {!   @FXBindable String firstName; !   @FXBindable String lastName = “Smith”;!}!!                                          Get and set valuesdef p = new Person()!def last = p.lastName!p.firstName = “Agent”!!                                                                47
Properties in GroovyFXpublic class Person {!   @FXBindable String firstName; !   @FXBindable String lastName = “Smith”;!}!!def p = new Person()!def last = p.lastName!                 Access underlying property forp.firstName = “Agent”!                           binding!textField(text: bind(p.lastNameProperty()))!!                                                                        48
Binding in GroovyFX@FXBindable  class  Time  {      Integer  hours      Integer  minutes      Integer  seconds        Double  hourAngle      Double  minuteAngle      Double  secondAngle        public  Time()  {          //  bind  the  angle  properties  to  the  clock  time          hourAngleProperty().bind((hoursProperty()  *  30.0)  +  (minutesProperty()  *  0.5))          minuteAngleProperty().bind(minutesProperty()  *  6.0)          secondAngleProperty().bind(secondsProperty()  *  6.0)      }  }                                                                                                                         49
TableView in JavaObservableList<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);!                                                                          50
TableView in GroovyFXdef 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) })!}!                                                                         51
Layout in JavaTextField 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);!                                                                52
Layout in GroovyFXsg.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")     }  }}                                                               53
Layout in GroovyFX                     54
Layout in GroovyFXgridPane(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")!}!                                                                              55
Layout in GroovyFX                     56
GroovyFX Supports…                     57
GroovyFX Supports…                     58
JavaFX With Clojure        Artwork by Augusto Sellhorn   http://sellmic.com/                                                            59
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!                                                     60
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)                                                                                              61
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)                                                                62
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)                                                                               63
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)                                                                                                         64
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)))))                                                                                                        65
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)))))                                                                                                        66
JavaFX With Scala                    67
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                                                                                    68
Why Scala?>    Shares many language features with JavaFX Script that make GUI     programming easier:     l    Static Type Checking – Catch your errors at compile time     l    Closures – Wrap behavior and pass it by reference     l    Declarative – Express the UI by describing what it should look like>    Scala also supports Type Safe DSLs!     l    Implicit Conversions – type safe class extension     l    Operator Overloading – with standard precedence rules     l    DelayedInit / @specialized – advanced language features                                                                                 69
Java vs. Scala DSLpublic  class  VanishingCircles  extends  Application  {                                                             object  VanishingCircles  extends  JFXApp  {                                                                                                                                     var  circles:  Seq[Circle]  =  null      public  static  void  main(String[]  args)  {                                                                    stage  =  new  Stage  {          Application.launch(args);                                                                                              title  =  "Vanishing  Circles"      }                                                                                                                              width  =  800                                                                                                                                     height  =  600      @Override                                                                                                                      scene  =  new  Scene  {      public  void  start(Stage  primaryStage)  {                                                                                fill  =  BLACK          primaryStage.setTitle("Vanishing  Circles");                                                                             circles  =  for  (i  <-­‐  0  until  50)  yield  new  Circle  {          Group  root  =  new  Group();                                                                                          centerX  =  random  *  800          Scene  scene  =  new  Scene(root,  800,  600,  Color.BLACK);                                                     centerY  =  random  *  600          List<Circle>  circles  =  new  ArrayList<Circle>();                                                                    radius  =  150          for  (int  i  =  0;  i  <  50;  i++)  {                                                                      fill  =  color(random,  random,  random,  .2)                       40 Lines                                                                                                                                      33 Lines            final  Circle  circle  =  new  Circle(150);                                                                  effect  =  new  BoxBlur(10,  10,  3)              circle.setCenterX(Math.random()  *  800);                                                                          strokeWidth  <==  when  (hover)  then  4  otherwise  0              circle.setCenterY(Math.random()  *  600);                                                                          stroke  =  WHITE              circle.setFill(new  Color(Math.random(),  Math.random(),  Math.random(),  .2));                                onMouseClicked  =  {              circle.setEffect(new  BoxBlur(10,  10,  3));                                                                         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();      }  }                                                                                                                                                                                                                                                       70
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)              }          }      }  }                                                                                                                       71
object  VanishingCircles  extends  JFXApp  {      stage  =  new  Stage  {          title  =  "Disappearing  Circles"          width  =  800          height  =  600   for JavaFX applications                                  Base class        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)              }          }      }  }                                                                                                                       72
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)              }          }      }  }                                                                                                                       73
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)              }          }      }  }                                                                                                                       74
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)              }          }      }  }                                                                                                                       75
Binding in ScalaInfix 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"                                                                                             76
Animation in Scalaval  timeline  =  new  Timeline  {      cycleCount  =  INDEFINITE      autoReverse  =  true      keyFrames  =  for  (circle  <-­‐  circles)  yield  at  (40  s)  {          Set(              circle.centerX  -­‐>  random  *  stage.width,              circle.centerY  -­‐>  random  *  stage.height          )      }  }  timeline.play();                                                                                                      77
JavaFX Script-like animationAnimation in Scala                        syntax: at (duration) {keyframes}val  timeline  =  new  Timeline  {      cycleCount  =  INDEFINITE      autoReverse  =  true      keyFrames  =  for  (circle  <-­‐  circles)  yield  at  (40  s)  {          Set(              circle.centerX  -­‐>  random  *  stage.width,              circle.centerY  -­‐>  random  *  stage.height          )      }  }  timeline.play();                                                                                                      78
Animation in Scalaval  timeline  =  new  Timeline  {      cycleCount  =  INDEFINITE      autoReverse  =  true      keyFrames  =  for  (circle  <-­‐  circles)  yield  at  (40  s)  {          Set(              circle.centerX  -­‐>  random  *  stage.width,              circle.centerY  -­‐>  random  *  stage.height          )      }  }                                                         Operator overloading for animationtimeline.play();                                                     syntax                                                                                                    79
Animation in Scalaval  timeline  =  new  Timeline  {      cycleCount  =  INDEFINITE      autoReverse  =  true      keyFrames  =  for  (circle  <-­‐  circles)  yield  at  (40  s)  {          Set(    circle.centerX  -­‐>  random  *  stage.width  tween  EASE_BOTH,    circle.centerY  -­‐>  random  *  stage.height  tween  EASE_IN          )      }  }  timeline.play();                             Optional tween                                                   syntax                                                                                                    80
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()       }                                                                  81
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}                                                                82
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()       }                                                                              83
TableView in ScalaFXdef 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)},   !    }!)}!                                                    84
JavaFX With Visage                     85
About Project Visage>    “Visage is a domain specific language (DSL) designed for the     express purpose of writing user interfaces.”>    Visage project goals:     l    Compile to JavaFX Java APIs     l    Evolve the Language (Annotations, Maps, etc.)     l    Support Other Toolkits>    Come join the team!>    For more info: http://visage-lang.org/                                                                    86
Java vs. Visage DSLpublic  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                       40 Lines                                                                                                                                      35 Lines            final  Circle  circle  =  new  Circle(150);                                                                      fill:  color(random(),  random(),  random(),  .2)              circle.setCenterX(Math.random()  *  800);                                                                              effect:  BoxBlur  {              circle.setCenterY(Math.random()  *  600);                                                                                  height:  10              circle.setFill(new  Color(Math.random(),  Math.random(),  Math.random(),  .2));                                        width:  10              circle.setEffect(new  BoxBlur(10,  10,  3));                                                                             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();      }  }                                                                                                                                                                                                                                                  87
How about JavaFX on… VisageStage  {      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                  }              }          }      }  }                                                                                 88
How about JavaFX on… VisageStage  {      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                  }              }          }      }  }                                                                                 89
How about JavaFX on… VisageStage  {      title:  "Vanishing  Circles"      Scene  {          width:  800          height:  600          fill:  BLACK          Group  {              circles  =  for  (i  in  [1..50])  {                  Circle  {                      centerX:  random()  *  800                      centerY:  random()  *  600                  }              }          }      }  }                                                                                 90
Visage is JavaFX Script++>    Default Parameters>    New Literal Syntax For:     l    Angles – 35deg,  4rad,  1turn       l    Colors – #DDCCBB,  #AA33AA|CC       l    Lengths – 5px,  2pt,  3in,  4sp  >    Null-check Dereference     l    var width = rect.!width>    Built-in Bindable Maps (coming soon!)     l    var fruitMap = ["red" : apple, "yellow" : banana]     l    var fruit = bind fruitMap["red"]                                                               91
Visage and JavaFX 2.0 are made for each other…>    Enhanced Binding     l    Retains lazy evaluation properties with additional expressive power>    Integrated Collections     l    Sequences and Maps automatically convert between JavaFX           Observable Lists/Maps>    Built-in Animation Syntax     l    Ties into JavaFX animation subsystem     l    Provides consistent, clean APIs                                                                                 92
Other JVM Languages to Try>    JRuby     l    Faithful to Ruby language with the power of the JVM>    Gosu     l    Up and coming language created at GuideWire     l    Easy to enhance libraries and create DSLs>    Mirah     l    Invented by Charles Nutter     l    Local Type Inference, Static and Dynamic Typing>    Fantom     l    Created by Brian and Andy Frank     l    Portable to Java and .NET     l    Local Type Inference, Static and Dynamic Typing                                                                 93
Conclusion>  You can write JavaFX applications in pure Java>  JavaFX is also usable in alternate languages>  You can get improved support using DSL libraries     l    GroovyFX     l    ScalaFX>    Or a dedicated UI JVM Language     l    Visage
Stephen Chin                                                            stephen.chin@oracle.com                                                            tweet: @steveonjavaThanks to Dean Iverson and Jonathan Giles for help preparing this talk          95

Recommended

PPTX
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
PPTX
Hacking JavaFX with Groovy, Clojure, Scala, and Visage
PPTX
JavaFX and Scala - Like Milk and Cookies
PPTX
ScalaDays 2014 - Reactive Scala 3D Game Engine
PPT
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
PPTX
Moving from JFreeChart to JavaFX with JavaFX Chart Extensions
PDF
Java FX 2.0 - A Developer's Guide
PPTX
JavaFX 2.0 With Alternative Languages - JavaOne 2011
PPTX
JavaFX and Scala in the Cloud
PDF
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
PDF
Alternate JVM Languages
PDF
The Ring programming language version 1.6 book - Part 46 of 189
PPTX
JavaFX 2.0 With Alternative Languages [Portuguese]
PDF
Zend Framework 1 + Doctrine 2
PDF
Scala in practice
PDF
Building node.js applications with Database Jones
PPTX
Php forum2015 tomas_final
PDF
Developing for Node.JS with MySQL and NoSQL
PDF
Java7 New Features and Code Examples
PDF
Scala vs Java 8 in a Java 8 World
 
PDF
Clojure: Functional Concurrency for the JVM (presented at OSCON)
PDF
The Ring programming language version 1.2 book - Part 79 of 84
PDF
The Ring programming language version 1.2 book - Part 32 of 84
PDF
Scala active record
PDF
Java 7 New Features
PDF
PHP and MySQL Tips and tricks, DC 2007
PDF
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
PDF
Scala ActiveRecord
PPTX
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
PPTX
Java Core | JavaFX 2.0: Great User Interfaces in Java | Simon Ritter

More Related Content

PPTX
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
PPTX
Hacking JavaFX with Groovy, Clojure, Scala, and Visage
PPTX
JavaFX and Scala - Like Milk and Cookies
PPTX
ScalaDays 2014 - Reactive Scala 3D Game Engine
PPT
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
PPTX
Moving from JFreeChart to JavaFX with JavaFX Chart Extensions
PDF
Java FX 2.0 - A Developer's Guide
PPTX
JavaFX 2.0 With Alternative Languages - JavaOne 2011
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
Hacking JavaFX with Groovy, Clojure, Scala, and Visage
JavaFX and Scala - Like Milk and Cookies
ScalaDays 2014 - Reactive Scala 3D Game Engine
Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
Moving from JFreeChart to JavaFX with JavaFX Chart Extensions
Java FX 2.0 - A Developer's Guide
JavaFX 2.0 With Alternative Languages - JavaOne 2011

What's hot

PPTX
JavaFX and Scala in the Cloud
PDF
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
PDF
Alternate JVM Languages
PDF
The Ring programming language version 1.6 book - Part 46 of 189
PPTX
JavaFX 2.0 With Alternative Languages [Portuguese]
PDF
Zend Framework 1 + Doctrine 2
PDF
Scala in practice
PDF
Building node.js applications with Database Jones
PPTX
Php forum2015 tomas_final
PDF
Developing for Node.JS with MySQL and NoSQL
PDF
Java7 New Features and Code Examples
PDF
Scala vs Java 8 in a Java 8 World
 
PDF
Clojure: Functional Concurrency for the JVM (presented at OSCON)
PDF
The Ring programming language version 1.2 book - Part 79 of 84
PDF
The Ring programming language version 1.2 book - Part 32 of 84
PDF
Scala active record
PDF
Java 7 New Features
PDF
PHP and MySQL Tips and tricks, DC 2007
PDF
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
PDF
Scala ActiveRecord
JavaFX and Scala in the Cloud
JavaFX Your Way: Building JavaFX Applications with Alternative Languages
Alternate JVM Languages
The Ring programming language version 1.6 book - Part 46 of 189
JavaFX 2.0 With Alternative Languages [Portuguese]
Zend Framework 1 + Doctrine 2
Scala in practice
Building node.js applications with Database Jones
Php forum2015 tomas_final
Developing for Node.JS with MySQL and NoSQL
Java7 New Features and Code Examples
Scala vs Java 8 in a Java 8 World
 
Clojure: Functional Concurrency for the JVM (presented at OSCON)
The Ring programming language version 1.2 book - Part 79 of 84
The Ring programming language version 1.2 book - Part 32 of 84
Scala active record
Java 7 New Features
PHP and MySQL Tips and tricks, DC 2007
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
Scala ActiveRecord

Similar to Hacking JavaFX with Groovy, Clojure, Scala, and Visage: Stephen Chin

PPTX
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
PPTX
Java Core | JavaFX 2.0: Great User Interfaces in Java | Simon Ritter
PPTX
JavaFX Your Way - Devoxx Version
PPTX
OpenJFX on Android and Devices
PDF
JavaFX 1.0 SDK Aquarium Paris
ODP
Java Fx Overview Tech Tour
PPT
Intro to JavaFX & Widget FX
PDF
JavaFX Overview
PDF
Javafx Overview 90minutes
PDF
Javafx Overview 90minutes
PDF
Javafx Overview 90minutes
PPT
JavaFX - Next Generation Java UI
PDF
Java Fx Ajaxworld Rags V1
PPTX
JavaFX 2.0 and Alternative Languages
PPTX
Raspberry Pi à la GroovyFX
PPTX
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
PDF
BeJUG JavaFx In Practice
ODP
JavaFX introduction
PDF
Moving to the Client - JavaFX and HTML5
PDF
JavaFX goes Scala
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
Java Core | JavaFX 2.0: Great User Interfaces in Java | Simon Ritter
JavaFX Your Way - Devoxx Version
OpenJFX on Android and Devices
JavaFX 1.0 SDK Aquarium Paris
Java Fx Overview Tech Tour
Intro to JavaFX & Widget FX
JavaFX Overview
Javafx Overview 90minutes
Javafx Overview 90minutes
Javafx Overview 90minutes
JavaFX - Next Generation Java UI
Java Fx Ajaxworld Rags V1
JavaFX 2.0 and Alternative Languages
Raspberry Pi à la GroovyFX
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
BeJUG JavaFx In Practice
JavaFX introduction
Moving to the Client - JavaFX and HTML5
JavaFX goes Scala

Recently uploaded

DOCX
iRobot Post‑Mortem and Alternative Paths - Discussion Document for Boards and...
PPTX
Building Cyber Resilience for 2026: Best Practices for a Secure, AI-Driven Bu...
PDF
Is It Possible to Have Wi-Fi Without an Internet Provider
PPTX
AI in Cybersecurity: Digital Defense by Yasir Naveed Riaz
PPTX
wob-report.pptxwob-report.pptxwob-report.pptx
PDF
The major tech developments for 2026 by Pluralsight, a research and training ...
PDF
The year in review - MarvelClient in 2025
PDF
Six Shifts For 2026 (And The Next Six Years)
PDF
GPUS and How to Program Them by Manya Bansal
PPTX
DYNAMICALLY.pptx good for the teachers or students to do seminars and for tea...
PDF
Session 1 - Solving Semi-Structured Documents with Document Understanding
PPTX
Protecting Data in an AI Driven World - Cybersecurity in 2026
PDF
Internet_of_Things_IoT_for_Next_Generation_Smart_Systems_Utilizing.pdf
PPTX
THIS IS CYBER SECURITY NOTES USED IN CLASS ON VARIOUS TOPICS USED IN CYBERSEC...
PDF
Our Digital Tribe_ Cultivating Connection and Growth in Our Slack Community 🌿...
PPTX
Cloud-and-AI-Platform-FY26-Partner-Playbook.pptx
PDF
Unser Jahresrückblick – MarvelClient in 2025
PDF
Real-Time Data Insight Using Microsoft Forms for Business
PPTX
Data Privacy and Protection: Safeguarding Information in a Connected World
PDF
Usage Control for Process Discovery through a Trusted Execution Environment
iRobot Post‑Mortem and Alternative Paths - Discussion Document for Boards and...
Building Cyber Resilience for 2026: Best Practices for a Secure, AI-Driven Bu...
Is It Possible to Have Wi-Fi Without an Internet Provider
AI in Cybersecurity: Digital Defense by Yasir Naveed Riaz
wob-report.pptxwob-report.pptxwob-report.pptx
The major tech developments for 2026 by Pluralsight, a research and training ...
The year in review - MarvelClient in 2025
Six Shifts For 2026 (And The Next Six Years)
GPUS and How to Program Them by Manya Bansal
DYNAMICALLY.pptx good for the teachers or students to do seminars and for tea...
Session 1 - Solving Semi-Structured Documents with Document Understanding
Protecting Data in an AI Driven World - Cybersecurity in 2026
Internet_of_Things_IoT_for_Next_Generation_Smart_Systems_Utilizing.pdf
THIS IS CYBER SECURITY NOTES USED IN CLASS ON VARIOUS TOPICS USED IN CYBERSEC...
Our Digital Tribe_ Cultivating Connection and Growth in Our Slack Community 🌿...
Cloud-and-AI-Platform-FY26-Partner-Playbook.pptx
Unser Jahresrückblick – MarvelClient in 2025
Real-Time Data Insight Using Microsoft Forms for Business
Data Privacy and Protection: Safeguarding Information in a Connected World
Usage Control for Process Discovery through a Trusted Execution Environment

Hacking JavaFX with Groovy, Clojure, Scala, and Visage: Stephen Chin

  • 1.
    Hacking JavaFX withGroovy, 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 l  JFXtras l  ScalaFX Motorcyclist l  Visage >  User Group Co-Leader l  Silicon Valley JavaFX User Group l  Streamed Live!
  • 3.
  • 4.
    JavaFX 2.0 PlatformImmersiveApplication ExperienceLeverage your Java skills with modern JavaFXAPIs>  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.
  • 5.
    JavaFX is NowOpen Source!Part of the OpenJDK ProjectControls available now, additional codeadded incrementallyProject Page:>  http://openjdk.java.net/projects/openjfx/ 5
  • 6.
    And Will Runon Tablets!*>  iPad (iOS)>  Linux (Popular Platform for Tablets That Runs Something Similar to Java) *No Release Timeline Announced Yet 6
  • 7.
  • 8.
    Programming Languages>  JavaFX 2.0 APIs are now in Java l  Pure Java APIs for all of JavaFX l  Binding and Sequences exposed as Java APIs l  FXML Markup for tooling>  Embrace all JVM languages l  Groovy, Scala, Clojure, JRuby l  Fantom, Mira, Gosu, Jython, etc.>  JavaFX Script is no longer supported by Oracle l  Existing JavaFX Script based applications will continue to run l  Visage is the open-source successor to the JavaFX Script language
  • 9.
    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
  • 10.
    Example Applicationpublic  class HelloStage  extends  Application  {        @Override  public  void  start(Stage  stage)  {          stage.setTitle("Hello  Stage");          stage.setWidth(600);          stage.setHeight(450);            Group  root  =  new  Group();          Scene  scene  =  new  Scene(root);          scene.setFill(Color.LIGHTGREEN);            stage.setScene(scene);          stage.show();      }        public  static  void  main(String[]  args)  {          Application.launch(args);      }  }  
  • 11.
    Example Application UsingBuilderspublic  class  HelloStage  extends  Application  {        @Override  public  void  start(Stage  stage)  {          stage.setTitle("Hello  Stage");          stage.setScene(SceneBuilder.create()              .fill(Color.LIGHTGREEN)              .width(600)              .height(450)          .build());          stage.show();      }        public  static  void  main(String[]  args)  {          Application.launch(args);      }  }  
  • 12.
    Observable Properties>  Supportswatching for changes to properties>  Implemented via anonymous inner classes>  Will take advantage of closures in the future
  • 13.
    Observable Pseudo-Properties  final Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);  rect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {                  });  
  • 14.
    Observable Pseudo-Properties  final Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);   The property we want to watchrect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {                });  
  • 15.
    Observable Pseudo-Properties  final Rectangle  rect  =  new  Rectangle();  rect.setX(40);   Only one listener used with generics torect.setY(40);   specify the data typerect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {                    });  
  • 16.
    Observable Pseudo-Properties  final Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);  rect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {      public  void  changed(ObservableValue<?  extends  Boolean>  property,  Boolean  oldValue,  Boolean  value)  {        }  });   Refers to the Rectangle.hoverProperty()
  • 17.
    Observable Pseudo-Properties  final Rectangle  rect  =  new  Rectangle();  rect.setX(40);  rect.setY(40);  rect.setWidth(100);  rect.setHeight(200);      rect.hoverProperty().addListener(new  ChangeListener<Boolean>()  {      public  void  changed(ObservableValue<?  extends  Boolean>  property,  Boolean  oldValue,  Boolean  value)  {          rect.setFill(rect.isHover()  ?  Color.GREEN  :  Color.RED);      }  });  
  • 18.
    Binding>  Unquestionably thebiggest JavaFX Script innovation>  Supported via a PropertyBinding class>  Lazy invocation for high performance>  Static construction syntax for simple cases l  e.g.: bind(<property>), bindBiDirectional(<property>)
  • 19.
    Sequences in Java>  Replaced with an Observable List>  Public API is based on JavaFX sequences>  Internal code can use lighter collections API>  JavaFX 2.0 also has an Observable Map
  • 20.
  • 21.
    Vanishing Circles inJavapublic  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++)  {   40 Lines            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));   1299 Characters            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();      }  }   21
  • 22.
    Application Skeletonpublic  class VanishingCircles  extends  Application  {      public  static  void  main(String[]  args)  {          Application.launch(args);      }      @Override      public  void  start(Stage  primaryStage)  {          primaryStage.setTitle("Vanishing  Circles");          Group  root  =  new  Group();          Scene  scene  =  new  Scene(root,  800,  600,  Color.BLACK);          [create  the  circles…]          root.getChildren().addAll(circles);          primaryStage.setScene(scene);          primaryStage.show();          [begin  the  animation…]      }  }  
  • 23.
    Create the CirclesList<Circle> circles  =  new  ArrayList<Circle>();  for  (int  i  =  0;  i  <  50;  i++)  {      final  Circle  circle  =  new  Circle(150);      circle.setCenterX(Math.random()  *  800);      circle.setCenterY(Math.random()  *  600);      circle.setFill(new  Color(Math.random(),  Math.random(),                                                        Math.random(),  .2));      circle.setEffect(new  BoxBlur(10,  10,  3));      circle.setStroke(Color.WHITE);      [setup  binding…]      [setup  event  listeners…]      circles.add(circle);  }   23
  • 24.
    Setup Bindingcircle.strokeWidthProperty().bind(Bindings     .when(circle.hoverProperty())      .then(4)      .otherwise(0)  );   24
  • 25.
    Setup Event Listenerscircle.addEventHandler(MouseEvent.MOUSE_CLICKED,                                                 new  EventHandler<MouseEvent>()  {      public  void  handle(MouseEvent  t)  {          KeyValue  collapse  =  new  KeyValue(circle.radiusProperty(),  0);          new  Timeline(new  KeyFrame(Duration.seconds(3),                                                                collapse)).play();      }  });   25
  • 26.
    Begin the AnimationTimeline moveCircles  =  new  Timeline();  for  (Circle  circle  :  circles)  {      KeyValue  moveX  =  new  KeyValue(circle.centerXProperty(),                                                                    Math.random()  *  800);      KeyValue  moveY  =  new  KeyValue(circle.centerYProperty(),                                                                    Math.random()  *  600);      moveCircles.getKeyFrames().add(new  KeyFrame(Duration.seconds(40),                                                                                              moveX,  moveY));  }  moveCircles.play();   26
  • 27.
  • 28.
    Features of Groovy>  Modern language l  Closures l  AST Transforms l  Strongly typed dynamic language>  Tight integration with Java l  Very easy to port from Java to Groovy>  Declarative syntax with GroovyFX Builders l  Familiar to Groovy and JavaFX Script developers
  • 29.
    Java vs. GroovyFXDSLpublic  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  -­‐>   40 Lines 29 Lines            final  Circle  circle  =  new  Circle(150);                          timeline  {              circle.setCenterX(Math.random()  *  800);                              at(3.s)  {  change  e.source.radiusProperty()  to  0  }              circle.setCenterY(Math.random()  *  600);                          }.play()              circle.setFill(new  Color(Math.random(),  Math.random(),  Math.random(),  .2));                      }   671 Characters            circle.setEffect(new  BoxBlur(10,  10,  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();              }          }            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();      }  }   29
  • 30.
    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)                  }              }          }      }  }   30
  • 31.
    GroovyFX.start  {  primaryStage -­‐>      def  sg  =  new  SceneGraphBuilder()      def  rand  =  new  Random().&nextInt      def  circles  =  []        sg.stage(title:  'Vanishing  Circles',  show:  true)  {   Builder for GroovyFX scene graphs        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)                  }              }          }      }  }   31
  • 32.
    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)                  }              }          }      }  }   32
  • 33.
    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)                  }              }          }      }  }   33
  • 34.
    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)                  }              }          }      }  }   34
  • 35.
    GroovyFX.start  {  primaryStage -­‐>      def  sg  =  new  SceneGraphBuilder()      def  rand  =  new  Random().&nextInt      def  circles  =  []        sg.stage(title:  'Vanishing  Circles',  show:  true)  {   Creation Via Loop Sequence        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)                  }              }          }      }  }   35
  • 36.
    Animation in GroovyFXtimeline(cycleCount: Timeline.INDEFINITE,  autoReverse:  true)  {      circles.each  {  circle  -­‐>          at  (40.s)  {              change  circle.centerXProperty()  to  rand(800)              change  circle.centerYProperty()  to  rand(600)          }      }  }.play()   36
  • 37.
    Animation in GroovyFXtimeline(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} 37
  • 38.
    Animation in GroovyFXtimeline(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 38
  • 39.
    Animation in GroovyFXtimeline(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 39
  • 40.
    Event Listeners inGroovyFX>  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()   }   40
  • 41.
    Event Listeners inGroovyFX>  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} 41
  • 42.
    Event Listeners inGroovyFX>  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()   }   42
  • 43.
    But wait, thereis more Grooviness… 43
  • 44.
    Properties in Javapublicclass 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.! } !}!   44
  • 45.
    Properties in GroovyFXpublicclass Person {! @FXBindable String firstName; ! @FXBindable String lastName;!}!   45
  • 46.
    Properties in GroovyFXpublicclass Person {! @FXBindable String firstName; ! @FXBindable String lastName = “Smith”;!}!   Optional initializers 46
  • 47.
    Properties in GroovyFXpublicclass Person {! @FXBindable String firstName; ! @FXBindable String lastName = “Smith”;!}!! Get and set valuesdef p = new Person()!def last = p.lastName!p.firstName = “Agent”!! 47
  • 48.
    Properties in GroovyFXpublicclass Person {! @FXBindable String firstName; ! @FXBindable String lastName = “Smith”;!}!!def p = new Person()!def last = p.lastName! Access underlying property forp.firstName = “Agent”! binding!textField(text: bind(p.lastNameProperty()))!! 48
  • 49.
    Binding in GroovyFX@FXBindable class  Time  {      Integer  hours      Integer  minutes      Integer  seconds        Double  hourAngle      Double  minuteAngle      Double  secondAngle        public  Time()  {          //  bind  the  angle  properties  to  the  clock  time          hourAngleProperty().bind((hoursProperty()  *  30.0)  +  (minutesProperty()  *  0.5))          minuteAngleProperty().bind(minutesProperty()  *  6.0)          secondAngleProperty().bind(secondsProperty()  *  6.0)      }  }   49
  • 50.
    TableView in JavaObservableList<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);! 50
  • 51.
    TableView in GroovyFXdefdateFormat = 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) })!}! 51
  • 52.
    Layout in JavaTextFieldurlField = 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);! 52
  • 53.
    Layout in GroovyFXsg.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") } }} 53
  • 54.
  • 55.
    Layout in GroovyFXgridPane(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")!}! 55
  • 56.
  • 57.
  • 58.
  • 59.
    JavaFX With Clojure Artwork by Augusto Sellhorn http://sellmic.com/ 59
  • 60.
    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! 60
  • 61.
    Clojure Syntax inOne 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) 61
  • 62.
    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)   62
  • 63.
    Refined Clojure GUIExample(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)   63
  • 64.
    Refined Clojure GUIExample(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)   64
  • 65.
    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)))))   65
  • 66.
    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)))))   66
  • 67.
  • 68.
    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 68
  • 69.
    Why Scala?>  Shares many language features with JavaFX Script that make GUI programming easier: l  Static Type Checking – Catch your errors at compile time l  Closures – Wrap behavior and pass it by reference l  Declarative – Express the UI by describing what it should look like>  Scala also supports Type Safe DSLs! l  Implicit Conversions – type safe class extension l  Operator Overloading – with standard precedence rules l  DelayedInit / @specialized – advanced language features 69
  • 70.
    Java vs. ScalaDSLpublic  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)   40 Lines 33 Lines            final  Circle  circle  =  new  Circle(150);                  effect  =  new  BoxBlur(10,  10,  3)              circle.setCenterX(Math.random()  *  800);                  strokeWidth  <==  when  (hover)  then  4  otherwise  0              circle.setCenterY(Math.random()  *  600);                  stroke  =  WHITE              circle.setFill(new  Color(Math.random(),  Math.random(),  Math.random(),  .2));                  onMouseClicked  =  {              circle.setEffect(new  BoxBlur(10,  10,  3));                      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();      }  }   70
  • 71.
    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)              }          }      }  }   71
  • 72.
    object  VanishingCircles  extends JFXApp  {      stage  =  new  Stage  {          title  =  "Disappearing  Circles"          width  =  800          height  =  600   for JavaFX applications Base class        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)              }          }      }  }   72
  • 73.
    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)              }          }      }  }   73
  • 74.
    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)              }          }      }  }   74
  • 75.
    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)              }          }      }  }   75
  • 76.
    Binding in ScalaInfixAddition/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"   76
  • 77.
    Animation in Scalaval timeline  =  new  Timeline  {      cycleCount  =  INDEFINITE      autoReverse  =  true      keyFrames  =  for  (circle  <-­‐  circles)  yield  at  (40  s)  {          Set(              circle.centerX  -­‐>  random  *  stage.width,              circle.centerY  -­‐>  random  *  stage.height          )      }  }  timeline.play();   77
  • 78.
    JavaFX Script-like animationAnimationin 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();   78
  • 79.
    Animation in Scalaval timeline  =  new  Timeline  {      cycleCount  =  INDEFINITE      autoReverse  =  true      keyFrames  =  for  (circle  <-­‐  circles)  yield  at  (40  s)  {          Set(              circle.centerX  -­‐>  random  *  stage.width,              circle.centerY  -­‐>  random  *  stage.height          )      }  }   Operator overloading for animationtimeline.play();   syntax 79
  • 80.
    Animation in Scalaval timeline  =  new  Timeline  {      cycleCount  =  INDEFINITE      autoReverse  =  true      keyFrames  =  for  (circle  <-­‐  circles)  yield  at  (40  s)  {          Set(    circle.centerX  -­‐>  random  *  stage.width  tween  EASE_BOTH,    circle.centerY  -­‐>  random  *  stage.height  tween  EASE_IN          )      }  }  timeline.play();   Optional tween syntax 80
  • 81.
    Event Listeners inScala>  Supported using the built-in Closure syntax>  Arguments for event objects>  100% type-safe onMouseClicked  =  {  (e:  MouseEvent)  =>      Timeline(at(3  s){radius-­‐>0}).play()   }   81
  • 82.
    Event Listeners inScala>  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} 82
  • 83.
    Event Listeners inScala>  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()   }   83
  • 84.
    TableView in ScalaFXdefdateFormat = 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)}, ! }!)}! 84
  • 85.
  • 86.
    About Project Visage>  “Visage is a domain specific language (DSL) designed for the express purpose of writing user interfaces.”>  Visage project goals: l  Compile to JavaFX Java APIs l  Evolve the Language (Annotations, Maps, etc.) l  Support Other Toolkits>  Come join the team!>  For more info: http://visage-lang.org/ 86
  • 87.
    Java vs. VisageDSLpublic  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   40 Lines 35 Lines            final  Circle  circle  =  new  Circle(150);                      fill:  color(random(),  random(),  random(),  .2)              circle.setCenterX(Math.random()  *  800);                      effect:  BoxBlur  {              circle.setCenterY(Math.random()  *  600);                          height:  10              circle.setFill(new  Color(Math.random(),  Math.random(),  Math.random(),  .2));                          width:  10              circle.setEffect(new  BoxBlur(10,  10,  3));                          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();      }  }   87
  • 88.
    How about JavaFXon… VisageStage  {      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                  }              }          }      }  }   88
  • 89.
    How about JavaFXon… VisageStage  {      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                  }              }          }      }  }   89
  • 90.
    How about JavaFXon… VisageStage  {      title:  "Vanishing  Circles"      Scene  {          width:  800          height:  600          fill:  BLACK          Group  {              circles  =  for  (i  in  [1..50])  {                  Circle  {                      centerX:  random()  *  800                      centerY:  random()  *  600                  }              }          }      }  }   90
  • 91.
    Visage is JavaFXScript++>  Default Parameters>  New Literal Syntax For: l  Angles – 35deg,  4rad,  1turn   l  Colors – #DDCCBB,  #AA33AA|CC   l  Lengths – 5px,  2pt,  3in,  4sp  >  Null-check Dereference l  var width = rect.!width>  Built-in Bindable Maps (coming soon!) l  var fruitMap = ["red" : apple, "yellow" : banana] l  var fruit = bind fruitMap["red"] 91
  • 92.
    Visage and JavaFX2.0 are made for each other…>  Enhanced Binding l  Retains lazy evaluation properties with additional expressive power>  Integrated Collections l  Sequences and Maps automatically convert between JavaFX Observable Lists/Maps>  Built-in Animation Syntax l  Ties into JavaFX animation subsystem l  Provides consistent, clean APIs 92
  • 93.
    Other JVM Languagesto Try>  JRuby l  Faithful to Ruby language with the power of the JVM>  Gosu l  Up and coming language created at GuideWire l  Easy to enhance libraries and create DSLs>  Mirah l  Invented by Charles Nutter l  Local Type Inference, Static and Dynamic Typing>  Fantom l  Created by Brian and Andy Frank l  Portable to Java and .NET l  Local Type Inference, Static and Dynamic Typing 93
  • 94.
    Conclusion>  You canwrite JavaFX applications in pure Java>  JavaFX is also usable in alternate languages>  You can get improved support using DSL libraries l  GroovyFX l  ScalaFX>  Or a dedicated UI JVM Language l  Visage
  • 95.
    Stephen Chin stephen.chin@oracle.com tweet: @steveonjavaThanks to Dean Iverson and Jonathan Giles for help preparing this talk 95

[8]ページ先頭

©2009-2025 Movatter.jp