JavaFX 2 is the next version of a revolutionary rich client platform for developing immersive desktop applications. One of the new features in JavaFX 2 is a set of pure Java APIs that can be used from any JVM language, opening up tremendous possibilities. This presentation demonstrates the potential of using JavaFX 2 together with alternative languages such as Groovy, Clojure, and Scala. It also will showcase the successor to JavaFX Script, Visage, a DSL with features specifically targeted at helping create clean UIs.
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
Hacking JavaFX with Groovy, Clojure, Scala, and Visage: Stephen Chin
1. Hacking JavaFX with Groovy, Clojure,
Scala, and Visage
Stephen Chin
Java Evangelist, Oracle
stephen.chin@oracle.com
tweet: @steveonjava
2. Meet the Presenter
Stephen Chin
> Java Evangelist, Oracle
> Author, Pro JavaFX Platform 2
Family Man
> Open Source Hacker
l JFXtras
l ScalaFX
Motorcyclist l Visage
> User Group Co-Leader
l Silicon Valley JavaFX
User Group
l Streamed Live!
4. JavaFX 2.0 Platform
Immersive Application Experience
Leverage your Java skills with modern JavaFX
APIs
> Cross-platform Animation, Video, Charting
> Integrate Java, JavaScript, and HTML5 in the
same application
> New graphics stack takes advantage of
hardware acceleration for 2D and 3D
applications
> Use your favorite IDE: NetBeans, Eclipse,
IntelliJ, etc.
5. JavaFX is Now Open Source!
Part of the OpenJDK Project
Controls available now, additional code
added incrementally
Project Page:
> http://openjdk.java.net/projects/openjfx/
5
6. And Will Run on Tablets!*
> iPad (iOS)
> Linux (Popular
Platform for
Tablets That
Runs Something
Similar to Java)
*No Release Timeline Announced Yet
6
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 Application
public
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 Using Builders
public
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
> Supports watching 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 watch
rect.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 to
rect.setY(40);
specify the data type
rect.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 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>)
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
21. Vanishing Circles in Java
public
class
VanishingCircles
extends
Application
{
public
static
void
main(String[]
args)
{
Application.launch(args);
}
@Override
public
void
start(Stage
primaryStage)
{
primaryStage.setTitle("Vanishing
Circles");
Group
root
=
new
Group();
Scene
scene
=
new
Scene(root,
800,
600,
Color.BLACK);
List<Circle>
circles
=
new
ArrayList<Circle>();
for
(int
i
=
0;
i
<
50;
i++)
{
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 Skeleton
public
class
VanishingCircles
extends
Application
{
public
static
void
main(String[]
args)
{
Application.launch(args);
}
@Override
public
void
start(Stage
primaryStage)
{
primaryStage.setTitle("Vanishing
Circles");
Group
root
=
new
Group();
Scene
scene
=
new
Scene(root,
800,
600,
Color.BLACK);
[create
the
circles…]
root.getChildren().addAll(circles);
primaryStage.setScene(scene);
primaryStage.show();
[begin
the
animation…]
}
}
23. Create the Circles
List<Circle>
circles
=
new
ArrayList<Circle>();
for
(int
i
=
0;
i
<
50;
i++)
{
final
Circle
circle
=
new
Circle(150);
circle.setCenterX(Math.random()
*
800);
circle.setCenterY(Math.random()
*
600);
circle.setFill(new
Color(Math.random(),
Math.random(),
Math.random(),
.2));
circle.setEffect(new
BoxBlur(10,
10,
3));
circle.setStroke(Color.WHITE);
[setup
binding…]
[setup
event
listeners…]
circles.add(circle);
}
23
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. GroovyFX DSL
public
class
VanishingCircles
extends
Application
{
GroovyFX.start
{
primaryStage
-‐>
def
sg
=
new
SceneGraphBuilder()
public
static
void
main(String[]
args)
{
def
rand
=
new
Random().&nextInt
Application.launch(args);
def
circles
=
[]
}
sg.stage(title:
'Vanishing
Circles',
show:
true)
{
@Override
scene(fill:
black,
width:
800,
height:
600)
{
public
void
start(Stage
primaryStage)
{
50.times
{
primaryStage.setTitle("Vanishing
Circles");
circles
<<
circle(centerX:
rand(800),
centerY:
rand(600),
radius:
150,
stroke:
white,
Group
root
=
new
Group();
strokeWidth:
bind('hover',
converter:
{val
-‐>
val
?
4
:
0}))
{
Scene
scene
=
new
Scene(root,
800,
600,
Color.BLACK);
fill
rgb(rand(255),
rand(255),
rand(255),
0.2)
List<Circle>
circles
=
new
ArrayList<Circle>();
effect
boxBlur(width:
10,
height:
10,
iterations:
3)
for
(int
i
=
0;
i
<
50;
i++)
{
onMouseClicked
{
e
-‐>
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
46. Properties in GroovyFX
public class Person {!
@FXBindable String firstName; !
@FXBindable String lastName = “Smith”;!
}!
Optional initializers
46
47. Properties in GroovyFX
public class Person {!
@FXBindable String firstName; !
@FXBindable String lastName = “Smith”;!
}!
! Get and set values
def p = new Person()!
def last = p.lastName!
p.firstName = “Agent”!
!
47
48. Properties in GroovyFX
public class Person {!
@FXBindable String firstName; !
@FXBindable String lastName = “Smith”;!
}!
!
def p = new Person()!
def last = p.lastName! Access underlying property for
p.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 Java
ObservableList<Person> items = ...!
TableView<Person> tableView = new TableView<Person>(items);!
!
TableColumn<Person,String> firstNameCol = !
new TableColumn<Person,String>("First Name");!
!
firstNameCol.setCellValueFactory(!
new Callback<CellDataFeatures<Person, String>, !
ObservableValue<String>>() {!
public ObservableValue<String> call(CellDataFeatures<Person, String> p) !
{!
return p.getValue().firstNameProperty();!
}!
});!
!
tableView.getColumns().add(firstNameCol);!
50
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 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
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
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. Scala DSL
public
class
VanishingCircles
extends
Application
{
object
VanishingCircles
extends
JFXApp
{
var
circles:
Seq[Circle]
=
null
public
static
void
main(String[]
args)
{
stage
=
new
Stage
{
Application.launch(args);
title
=
"Vanishing
Circles"
}
width
=
800
height
=
600
@Override
scene
=
new
Scene
{
public
void
start(Stage
primaryStage)
{
fill
=
BLACK
primaryStage.setTitle("Vanishing
Circles");
circles
=
for
(i
<-‐
0
until
50)
yield
new
Circle
{
Group
root
=
new
Group();
centerX
=
random
*
800
Scene
scene
=
new
Scene(root,
800,
600,
Color.BLACK);
centerY
=
random
*
600
List<Circle>
circles
=
new
ArrayList<Circle>();
radius
=
150
for
(int
i
=
0;
i
<
50;
i++)
{
fill
=
color(random,
random,
random,
.2)
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 Scala
Infix Addition/Subtraction/Multiplication/Division:
height
<==
rect1.height
+
rect2.height
Aggregate Operators:
width
<==
max(rect1.width,
rect2.width,
rect3.width)
Conditional Expressions:
strokeWidth
<==
when
(hover)
then
4
otherwise
0
Compound Expressions:
text
<==
when
(rect.hover
||
circle.hover
&&
!disabled)
then
textField.text
+
"
is
enabled"
otherwise
"disabled"
76
77. Animation in Scala
val
timeline
=
new
Timeline
{
cycleCount
=
INDEFINITE
autoReverse
=
true
keyFrames
=
for
(circle
<-‐
circles)
yield
at
(40
s)
{
Set(
circle.centerX
-‐>
random
*
stage.width,
circle.centerY
-‐>
random
*
stage.height
)
}
}
timeline.play();
77
78. JavaFX Script-like animation
Animation in Scala syntax: at (duration) {keyframes}
val
timeline
=
new
Timeline
{
cycleCount
=
INDEFINITE
autoReverse
=
true
keyFrames
=
for
(circle
<-‐
circles)
yield
at
(40
s)
{
Set(
circle.centerX
-‐>
random
*
stage.width,
circle.centerY
-‐>
random
*
stage.height
)
}
}
timeline.play();
78
79. Animation in Scala
val
timeline
=
new
Timeline
{
cycleCount
=
INDEFINITE
autoReverse
=
true
keyFrames
=
for
(circle
<-‐
circles)
yield
at
(40
s)
{
Set(
circle.centerX
-‐>
random
*
stage.width,
circle.centerY
-‐>
random
*
stage.height
)
}
}
Operator overloading for animation
timeline.play();
syntax
79
80. Animation in Scala
val
timeline
=
new
Timeline
{
cycleCount
=
INDEFINITE
autoReverse
=
true
keyFrames
=
for
(circle
<-‐
circles)
yield
at
(40
s)
{
Set(
circle.centerX
-‐>
random
*
stage.width
tween
EASE_BOTH,
circle.centerY
-‐>
random
*
stage.height
tween
EASE_IN
)
}
}
timeline.play();
Optional tween
syntax
80
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()
}
81
82. 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
83. 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
84. TableView in ScalaFX
def dateFormat = new SimpleDateFormat("yyyy-MM-dd")!
new TableView[Speaker](persons) {!
columns = Seq(!
new TableColumn[Speaker, String] {!
text: "Name"!
converter = {_.firstName}!
} new TableColumn[Speaker, String] {!
text: "Age"!
converter = {_.age}!
}!
new TableColumn[Speaker, String] {!
text: "Gender"!
converter = {_.gender}!
}!
new TableColumn[Speaker, String] {!
text: "Birth"!
converter = {dateFormat.format(_.dob)}, !
}!
)}! 84
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. Visage DSL
public
class
VanishingCircles
extends
Application
{
var
circles:Circle[];
Stage
{
public
static
void
main(String[]
args)
{
title:
"Vanishing
Circles"
Application.launch(args);
Scene
{
}
width:
800
height:
600
@Override
fill:
BLACK
public
void
start(Stage
primaryStage)
{
Group
{
primaryStage.setTitle("Vanishing
Circles");
circles
=
for
(i
in
[1..50])
{
Group
root
=
new
Group();
def
c:Circle
=
Circle
{
Scene
scene
=
new
Scene(root,
800,
600,
Color.BLACK);
centerX:
random()
*
800
List<Circle>
circles
=
new
ArrayList<Circle>();
centerY:
random()
*
600
for
(int
i
=
0;
i
<
50;
i++)
{
radius:
150
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 JavaFX on… Visage
Stage
{
title:
"Vanishing
Circles"
scene:
Scene
{
width:
800
height:
600
fill:
BLACK
content:
Group
{
circles
=
for
(i
in
[1..50])
{
Circle
{
centerX:
random()
*
800
centerY:
random()
*
600
}
}
}
}
}
88
89. How about JavaFX on… Visage
Stage
{
title:
"Vanishing
Circles"
scene:
Scene
{
width:
800
height:
600
fill:
BLACK
content:
Group
{
circles
=
for
(i
in
[1..50])
{
Circle
{
centerX:
random()
*
800
centerY:
random()
*
600
}
}
}
}
}
89
90. How about JavaFX on… Visage
Stage
{
title:
"Vanishing
Circles"
Scene
{
width:
800
height:
600
fill:
BLACK
Group
{
circles
=
for
(i
in
[1..50])
{
Circle
{
centerX:
random()
*
800
centerY:
random()
*
600
}
}
}
}
}
90
92. 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
93. 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
94. 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
95. Stephen Chin
stephen.chin@oracle.com
tweet: @steveonjava
Thanks to Dean Iverson and Jonathan Giles for help preparing this talk 95