Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

70
Cleaner APIs, Cleaner UIs with Visage Stephen Chin Chief Agile Methodologist – GXS http://steveonjava.com/ Tweet: @steveonjava

description

Visage is a JVM language designed specifically for UI development, with special syntax for hierarchically describing UIs, binding data and behavior, and representing UI specific concepts such as animation, layout, and styles. It also is a full-featured language with a full compiler tool-chain, static compilation to JVM bytecodes, and IDE plug-ins. This talk will demonstrate how to use the Visage language to build UIs for JavaFX 2.0, Vaadin, and Android. Find out how you can take control of your UI development by writing cleaner, more maintainable UI code using the Visage language in your existing Java projects.

Transcript of Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Page 1: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Cleaner APIs, Cleaner UIs with VisageStephen ChinChief Agile Methodologist – GXS

http://steveonjava.com/Tweet: @steveonjava

Page 2: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

The Visage Language

2

Statically Compiled Language

Based on F3 / JavaFX Script

Planning Support for Different Platforms:- JavaFX 2.0- Vaadin- A Popular Linux-based

Tablet OS

> "Visage is a domain specific language (DSL) designed for the express purpose of writing user interfaces."

http://visage-lang.org/

Page 3: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

What Does Visage Look Like?Stage { var input:TextBox;  title: bind input.text  Scene {    input = TextBox { color: #DDCC33    }  }}

3

Page 4: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

What Does Visage Look Like?

4

Stage { var input:TextBox;  title: bind input.text  Scene {    input = TextBox { color: #DDCC33    }  }}

Hierarchy Models Your User Interface

Hierarchy Models Your User Interface

Page 5: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

What Does Visage Look Like?

5

Stage { var input:TextBox;  title: bind input.text  Scene {    input = TextBox { color: #DDCC33    }  }}

User Interface Updates Automatically

User Interface Updates Automatically

Page 6: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

What Does Visage Look Like?

6

Stage { var input:TextBox;  title: bind input.text  Scene {    input = TextBox { color: #DDCC33    }  }}

Built-in Constructs for Building UIs

Built-in Constructs for Building UIs

Page 7: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

What Does Visage Look Like?

7

Stage { var input:TextBox;  title: bind input.text  Scene {    input = TextBox { color: #DDCC33    }  }}

No more NPEs!No more NPEs!

Page 8: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

What Does Visage Look Like?

8

Stage { var input:TextBox;  title: bind input!.text  Scene {    input = TextBox { color: #DDCC33    }  }}

Unless you add an exclamation mark!Unless you add an exclamation mark!

Page 9: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

9

JavaFX With Visage

Page 10: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Java vs. Visage DSLpublic class VanishingCircles extends Application {

public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Vanishing Circles"); Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); List<Circle> circles = new ArrayList<Circle>(); for (int i = 0; i < 50; i++) { final Circle circle = new Circle(150); circle.setCenterX(Math.random() * 800); circle.setCenterY(Math.random() * 600); circle.setFill(new Color(Math.random(), Math.random(),

Math.random(), .2)); circle.setEffect(new BoxBlur(10, 10, 3)); circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new

EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); } }); circle.setStroke(Color.WHITE); circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) .then(4) .otherwise(0)); circles.add(circle); } root.getChildren().addAll(circles); primaryStage.setScene(scene); primaryStage.show(); Timeline moveCircles = new Timeline(); for (Circle circle : circles) { KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random() *

800); KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random() *

600); moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40), moveX,

moveY)); } moveCircles.play(); }}

var circles:Circle[];Stage { title: "Vanishing Circles" Scene { width: 800 height: 600 fill: BLACK Group { circles = for (i in [1..50]) { def c:Circle = Circle { centerX: random() * 800 centerY: random() * 600 radius: 150 fill: color(random(), random(), random(), .2) effect: BoxBlur { height: 10 width: 10 iterations: 3 } stroke: WHITE strokeWidth: bind if (c.hover) 5 else 0 onMouseClicked: function(e) { Timeline {at (3s) {c.radius => 0}}.play() } } } } }}

Timeline { for (circle in circles) at (40s) { circle.centerX => random() * 800; circle.centerY => random() * 600 }}.play()

10

40 Lines1299 Characters40 Lines1299 Characters

35 Lines487 Characters35 Lines487 Characters

Page 11: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

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 } } } }}

11

Page 12: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

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 } } } }}

12

Page 13: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

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 } } } }}

13

Page 14: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Vanishing Circles Demo

Page 15: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage is JavaFX Script++Default ParametersNew Literal Syntax For:- Angles – 35deg, 4rad, 1turn

- Colors – #DDCCBB, #AA33AA|CC

- Lengths – 5px, 2pt, 3in, 4spNull-check Dereference- var width = rect.!width

Built-in Bindable Maps (coming soon!)- var fruitMap = ["red" : apple, "yellow" : banana]

- var fruit = bind fruitMap["red"]

15

Page 16: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

And Made for JavaFX 2.0…Enhanced Binding- Retains lazy evaluation properties with additional

expressive power Integrated Collections- Sequences and Maps automatically convert between

JavaFX Observable Lists/MapsBuilt-in Animation Syntax- Ties into JavaFX animation subsystem

- Provides consistent, clean APIs

16

Page 17: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)
Page 18: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Why VaadinRich Set of UI Controls

Java->Javascript Compiler- Write Custom UI Components in Visage

Vaadin Brings Visage to the Web!

Page 19: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Java Vaadin Applicationpublic class SimpleApplication extends Application {

@Override

public void init() {

Window mainWindow = new Window("Simple Application");

Label label = new Label("Hello 33rd Degrees!");

mainWindow.addComponent(label);

setMainWindow(mainWindow);

}

}

19

Page 20: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage Vaadin Application (unoptimized)

public class VisagevaadinApplication extends Application {

override function init() {

def mainWindow = new Window("Unoptimized Visage Vaadin Application");

def label = new Label("Hello Visage User");

mainWindow.addComponent(label);

setMainWindow(mainWindow);

}

}

20

Page 21: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage Vaadin Application (optimized!)

public class VisagevaadinApplication2 extends Application {

override var window = Window {

caption: "Optimized Visage Vaadin Application"

Label {

content: "Hello Expert Visage Coder"

}

}

}

21

Page 22: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage Vaadin Address Book Demo

Page 23: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

A Popular Linux-based Tablet OS With VISAGE

Page 24: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage on Android

24

Visage Runs as a Native App on AndroidFull Access to all the Android APIsDeclarative Layer on Top of Android APIs

Page 25: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Hello World, Visage

Demo 1

25

Page 26: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Android XML Code<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" ><TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Hello World, HelloVisage" /></LinearLayout>

26

Page 27: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Plus some more Java…public class HelloVisage extends Activity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedIS) {

super.onCreate(savedIS);

setContentView(R.layout.main);

}

}

27

Page 28: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

All Java Conversion

28

Page 29: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Converted XML Code (simplified)public class HelloVisage extends Activity { @Override public void onCreate(Bundle savedIS) { super.onCreate(savedIS); Context context = getApplicationContext(); LinearLayout layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); TextView text = new TextView(context); text.setText("Hello World, Java Only"); layout.addView(text); setContentView(layout); }}

29

Page 30: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage Port

30

Page 31: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Straight JavaFX Conversion...public class HelloVisage extends Activity { override function onCreate(savedInstanceState:Bundle) { super.onCreate(savedInstanceState); def context = getApplicationContext(); def layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); def text = new TextView(context); text.setText("Hello World, Long Visage"); layout.addView(text); setContentView(layout); }}

31

Page 32: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Straight JavaFX Conversion...public class HelloVisage extends Activity { override function onCreate(savedInstanceState:Bundle) { super.onCreate(savedInstanceState); def context = getApplicationContext(); def layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); def text = new TextView(context); text.setText("Hello World, Long Visage"); layout.addView(text); setContentView(layout); }}

32

Override is a built-in keyword

Override is a built-in keyword

Page 33: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Straight JavaFX Conversion...public class HelloVisage extends Activity { override function onCreate(savedInstanceState:Bundle) { super.onCreate(savedInstanceState); def context = getApplicationContext(); def layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); def text = new TextView(context); text.setText("Hello World, Long Visage"); layout.addView(text); setContentView(layout); }}

33

Functions begin with the keyword "function"

Functions begin with the keyword "function"

Page 34: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Straight JavaFX Conversion...public class HelloVisage extends Activity { override function onCreate(savedInstanceState:Bundle) { super.onCreate(savedInstanceState); def context = getApplicationContext(); def layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); def text = new TextView(context); text.setText("Hello World, Long Visage"); layout.addView(text); setContentView(layout); }}

34

Type after variable (separated by colon)Type after variable

(separated by colon)

Page 35: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Straight JavaFX Conversion...public class HelloVisage extends Activity { override function onCreate(savedInstanceState:Bundle) { super.onCreate(savedInstanceState); def context = getApplicationContext(); def layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); def text = new TextView(context); text.setText("Hello World, Long Visage"); layout.addView(text); setContentView(layout); }}

35

Variables declarations start with "def" or "var"Variables declarations start with "def" or "var"

Page 36: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Android JavaFX Codepublic class HelloVisage extends Activity {

override var view = LinearLayout {

orientation: Orientation.VERTICAL

view: TextView {

text: "Hello World, Beautified Visage"

}

}

}

36

Page 37: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Working Hello Visage Application

37

Page 38: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

CONTROLS AND SETTINGS

Demo 2

38

Page 39: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Android ControlsCreate a Text FieldCreate an Edit BoxWire them up using Binding

39

Page 40: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Bound Controls (1)override var view = LinearLayout {

orientation: Orientation.VERTICAL

var secret:String;

view: [

EditText {

hint: "Super Secret Text"

password: true

text: bind secret with inverse

40

Page 41: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Bound Controls (2) }

TextView {

text: "Is Revealed!!!"

}

TextView {

text: bind secret

}

]

}

41

Page 42: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Button HandlerCreate a Button ControlAdd an onClick handlerMake something happen (maybe a bind)

42

Page 43: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Button onClick handlerButton { text: "Launch Settings" onClick: function() { startActivity(new Intent(this, Settings.class)); setting = "Launching..."; }}TextView { text: "Setting is:"}TextView { text: bind setting}

43

Page 44: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Android SettingsCreate a Settings ActivityPopulate it with the following preferences:- Text

- Password

- ListLaunch it from the Button control

44

Page 45: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Settings Classpublic class Settings extends PreferenceActivity {

var usernamePref:EditTextPreference;

var passwordPref:EditTextPreference;

var pollingPref:ListPreference;

override var screen = PreferenceScreen {

preferences: [

45

Page 46: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Text PreferencePreferenceCategory {

title: "Preferences"

preferences: [

usernamePref = EditTextPreference {

title: "Username"

key: "usernamePref"

summary: bind if (usernamePref.text == "") "Currently undefined" else "Current value: {usernamePref.text}"

}46

Page 47: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Password PreferencepasswordPref = EditTextPreference {

title: "Password"

key: "passwordPref"

summary: bind passwordPref.text.replaceAll(".", "*");

}

47

Page 48: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

List PreferencepollingPref = ListPreference { title: "Polling Interval" key: "pollingPref" defaultValue: "60000" entries: ["30 seconds", "1 minute", "5 minutes", "10 minutes", "15 minutes", "30 minutes", "1 hour"] entryValues: ["30000", "60000", "300000", "600000", "900000", "1800000", "3600000"] summary: bind pollingPref.entry}

48

Page 49: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Working Settings Panel

49

Page 50: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Sequence Puzzlers

What is the size of this sequence: [1..10 step -1]

What does this evaluate to: [10..<20 step 2][k|k>17]

What is the size of this sequence:sizeof [20..1 step -3]

Page 51: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Thank You!Stephen Chinhttp://steveonjava.comTweet: @steveonjava

Visage Project: http://visage-lang.org/

51

Page 52: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage Language Fundamentals

Lesson 1

52

Page 53: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Datatype Support

53

Page 54: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage Operators

54

> Multiplication and division of two durations is allowed, but not meaningful

> Underflows/Overflows will fail silently, producing inaccurate results

> Divide by zero will throw a runtime exception

Page 55: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage Operators (continued)

55

Page 56: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Access Modifiers

56

Page 57: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Data BindingA variable or a constant can be bound to an

expression- var x = bind a + b;

The bound expression is rememberedThe dependencies of the expression is watchedVariable is updated lazily when possible

57

Page 58: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

What Bind Updatesvar x = bind if(a) then b else cx is updated if a or b or c changes

var x = bind for (i in [a..b]) { i * i }Not everything is recalculated If a = 1 and b = 2, x is [1, 4] If b changes to 3, only the added element is

calculated

58

11 44 99

Page 59: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Binding to ExpressionsBinding to a blockBound block may contain any number of defs

followed by one expressionDependencies of block is backtraced from the

expressionBinding to function invocation expression- Regular function: dependencies are parameters

- Bound function: backtraced from final expression inside function

59

Page 60: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Binding to Object Literalsvar a = 3; var b = 4;var p = bind Point { x: a, y: b };var q = bind Point { x: bind a, y: b };var r = bind Point { x: bind a, y: bind b };

When a changes:- p gets a new instance of Point- q and r keep the old instance with a new x value- r will never get a new instance of Point

- (the outer bind in r is useless)

60

Page 61: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Integrating Visage and JavaCalling Java from Visage- Can call Java interface or classes directly

- Automatic conversion to and from Arrays and Collections

- Can even extend Java interfaces and classesCalling Visage from Java- Easiest way is to create a Java interface that Visage

extends

- Can invoke Visage as a script and get results back

61

Page 62: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Advanced Javafx sequences

Lesson 2

62

Page 63: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Visage SequencesRepresents collections of homogeneous dataA fundamental container data typeRich set of language facilitiesContributor to declarative syntaxAutomatic conversion to and from Java Arrays

and Collections

63

Page 64: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Creating SequencesExplicit sequence expression- [1, 3, 5, 7, 9]

Elements are separated by commasComma may be omitted if element ends with

brace

64

11 33 55 77 99

Page 65: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Creating SequencesNumeric sequence with range expressions:- [1..10]

Can have a step:- [1..10 step 2]- [0.0..0.9 step 0.1]

Can be decreasing:- [10..1 step -3]

Beware of step that goes opposite direction:- [10..1] is []

Exclusive right end- [1..<5]

11 22 33 44 55 66 77 88 99 1010

11 33 55 77 99

00 .1.1 .2.2 .3.3 .4.4 .5.5 .6.6 .7.7 .8.8 .9.9

1010 77 44 11

11 22 33 44

Page 66: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Getting Info from Sequencesints = [1, 3, 5, 7, 9]

sizeof ints is 5 ints[0] is 1, ints[1] is 3, ..., ints[4] is 9 ints[-1] is 0 (default value of Integer), so is ints[5]

Object sequence has a default of null

66

11 33 55 77 99[0] [1] [2] [3] [4]

Page 67: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Getting Slices from Sequencesints = [1, 3, 5, 7, 9]

ints[0..2] is [1, 3, 5] ints[0..<2] is [1, 3] ints[2..] is [5, 7, 9] ints[2..<] is [5, 7] ints[2..0], ints[-2..-1], ints[5..6] are all []s

67

11 33 55 77 99[0] [1] [2] [3] [4]

Page 68: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Getting Subsets from Sequencesints = [1, 3, 5, 7, 9]

ints[k | k > 6] is:- [7, 9] (k > 6 is a condition)

ints[k | indexof k < 2] is:- [1, 3]

ints[k | k > 10] is:- []

68

11 33 55 77 99[0] [1] [2] [3] [4]

Page 69: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Inserting into Sequencesints = [1, 3, 5, 7, 9]

insert 20 into ints

insert 30 before ints[2]

insert 40 after ints[4]

insert [50, 60] into ints

11 33 55 77 99

11 33 55 77 99

11 33 55 77 99

2020

20203030

11 33 55 77 99 20203030 4040

11 33 55 77 99 20203030 4040 5050 6060

Page 70: Cleaner APIs, Cleaner UIs with Visage (33rd Degrees)

Deleting from Sequences ints = [1, 3, 5, 7, 9]

delete 7 from ints

delete ints[0]

delete ints[0..1]

delete ints: ints becomes []

11 33 55 77 99

11 33 55 99

33 55 99

99

11 33 55 77 99