Fluent API code formatting in Netbeans 7.2 beta

I always missed a way to format Fluent API code blocks in Netbeans properly.

In Netbeans 7.1 at last there was an option to set the wrapping for chained method calls via:
Preferences: Editor -> Language: Java -> Category: Wrapping

Netbeans 7.1






Netbeans 7.2

Netbeans 7.2 now offers an additional option to choose whether the wrapping should take place:

First steps with JavaFX

This tutorial will introduce

  • Clean separation of application UI and logic
  • Using Builders supporting Fluent API
  • Effects
  • Layouts
  • Property Bindings
  • EventHandler

Abstract

The Golden Ratio (Golden MeanGolden Section) is defined as φ = (√5 + 1) / 2.

Objective

Create an UI to find the golden section by using a slider. Add value labels and an indicator to get green when golden action is hit.

The Model

Create observable double a and b values and a boolean value to indicate whether the golden section has been hit:

private DoubleProperty aValue = new SimpleDoubleProperty(0.0);
private DoubleProperty bValue = new SimpleDoubleProperty(0.0);
private BooleanProperty sectioAurea = new SimpleBooleanProperty(false);

Method to calculate the golden section:

private void reCalculate(){

double b = aValue.substract(100).multiply(-1).get();
setBValue(b);

double result1 = aValue.get()/bValue.get();
result1 = Math.round(result1*100.0)/100.0;

double result2 = (aValue.get()+bValue.get())/aValue.get();
result2 = Math.round(result2*100.0)/100.0;

sectioAurea.set(result1 == result2);
}

Please note: get() returns the primitive double value, getValue() respectively returns the Double instance.

Next attach listeners to re-caculate the golden section indicator on value changes:

private void attachListener(){ChangeListener onChangeListener = new ChangeListener(){
@Override
public void changed(ObservableValue arg0, Number arg1, Number arg2) {
reCalculate();
}
};

getAValueProperty().addListener(onChangeListener);
getBValueProperty().addListener(onChangeListener);
}

Here is the complete model code width appropriate getter and setter methods:

package de.jensd.javafx.aurea;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

/**
*
* @author Jens Deters
*/
public class SectioModel {

private DoubleProperty aValue = new SimpleDoubleProperty(0.0);
private DoubleProperty bValue = new SimpleDoubleProperty(0.0);
private BooleanProperty sectioAurea = new SimpleBooleanProperty(false);

public SectioModel() {
attachListener();
}

private void attachListener(){

ChangeListener onChangeListener = new ChangeListener(){
@Override
public void changed(ObservableValue arg0, Number arg1, Number arg2) {
reCalculate();
}
};

getAValueProperty().addListener(onChangeListener);
getBValueProperty().addListener(onChangeListener);
}

public DoubleProperty getAValueProperty() {
return aValue;
}

public double getAValue() {
return aValue.get();
}

public void setAValue(double d) {
aValue.set(d);
}

public DoubleProperty getBValueProperty() {
return bValue;
}

public double getBValue() {
return bValue.get();
}

public void setBValue(double d) {
bValue.set(d);
}

public BooleanProperty getSectioAureaProperty() {
return sectioAurea;
}

public boolean isSectioAurea() {
return sectioAurea.get();
}

private void reCalculate(){

double b = aValue.substract(100).multiply(-1).get();
setBValue(b);

double result1 = aValue.get()/bValue.get();
result1 = Math.round(result1*100.0)/100.0;

double result2 = (aValue.get()+bValue.get())/aValue.get();
result2 = Math.round(result2*100.0)/100.0;

sectioAurea.set(result1 == result2);
}
}

The UI 

Title Label and usage hint

Text titleText = TextBuilder.create().
text("Sectio Aurea").
effect(ds).
font(Font.font(null, FontWeight.BOLD, 32)).
build();

Text descriptionText = TextBuilder.create().
text("Move the slider until your hit the golden section.\nHint: use the cursor keys for exact control.").
textAlignment(TextAlignment.CENTER).
build();

Indicator

Stop[] falseStops = new Stop[]{new Stop(0.0, Color.WHITE), new Stop(0.3, Color.RED), new Stop(1.0, Color.DARKRED)};
falsePaint = new RadialGradient(0.0, 0.0, -10.0, -10.0, 50, false, CycleMethod.NO_CYCLE, falseStops);

Stop[] trueStops = new Stop[]{new Stop(0.0, Color.WHITE), new Stop(0.3, Color.GREENYELLOW), new Stop(1.0, Color.DARKGREEN)};
truePaint = new RadialGradient(0.0, 0.0, -10.0, -10.0, 50, false, CycleMethod.NO_CYCLE, trueStops);

ellipse = EllipseBuilder.create().
radiusX(30).
radiusY(30).
fill(falsePaint).
build();

DropShadow ds = new DropShadow();
ds.setOffsetY(3.0f);
ds.setColor(Color.color(0.4f, 0.4f, 0.4f));

ellipse.setEffect(ds);

Slider

slider = SliderBuilder.create().
min(model.getAValue()).
max(model.getBValue()).
showTickMarks(true).
showTickLabels(true).
majorTickUnit(20).
minorTickCount(3).
prefWidth(300).
blockIncrement(0.1f).
build();

Status Labels

aValueText = TextBuilder.create().build();
bValueText = TextBuilder.create().build();
aureaText = TextBuilder.create().build();

Solve Button

solveButton = ButtonBuilder.create().text("Solve").build();

Add the solve action

solveButton.setOnAction(new EventHandler() {
@Override
public void handle(ActionEvent arg0) {
model.setAValue(61.8);
}
});

Put it all together

Using the GridPane layout:

GridPane root = new GridPane();
root.setPadding(new Insets(20, 20, 20, 20));
root.setGridLinesVisible(true);</h2>
RowConstraints rowInfo = new RowConstraints(100);
ColumnConstraints colInfo = new ColumnConstraints(100);

root.getRowConstraints().
add(rowInfo);

for (int j = 0; j &lt;= 2; j++) {
root.getColumnConstraints().
add(colInfo);
}

GridPane.setHalignment(titleText, HPos.CENTER);
GridPane.setHalignment(descriptionText, HPos.CENTER);
GridPane.setHalignment(slider, HPos.CENTER);
GridPane.setHalignment(aValueText, HPos.CENTER);
GridPane.setHalignment(ellipse, HPos.CENTER);
GridPane.setHalignment(bValueText, HPos.CENTER);
GridPane.setHalignment(solveButton, HPos.CENTER);

GridPane.setConstraints(titleText, 0, 0, 3, 1);
GridPane.setConstraints(descriptionText, 0, 1, 3, 1);
GridPane.setConstraints(slider, 0, 2, 3, 1);
GridPane.setConstraints(aValueText, 0, 3);
GridPane.setConstraints(ellipse, 1, 3);
GridPane.setConstraints(bValueText, 2, 3);
GridPane.setConstraints(solveButton, 2, 4);

Connect UI to Model

model.getAValueProperty().
bindBidirectional(slider.valueProperty());

aValueText.textProperty().
bind(model.getAValueProperty().
asString("%.1f"));
bValueText.textProperty().
bind(model.getBValueProperty().
asString("%.1f"));

aureaText.textProperty().
bind(model.getSectioAureaProperty().
asString());
<h2>Connect listener to refresh the indicator:</h2>
<pre>model.getSectioAureaProperty().
            addListener(new ChangeListener() {</pre>
@Override
public void changed(ObservableValue arg0, Boolean arg1, Boolean arg2) {
refreshIndicator();
}
});

The complete UI code

package de.jensd.javafx.aurea;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.control.Slider;
import javafx.scene.control.SliderBuilder;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.Paint;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.EllipseBuilder;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBuilder;
import javafx.stage.Stage;

/**
*
* @author Jens Deters
*/
public class SectioAureaApplication extends Application {

private SectioModel model = new SectioModel();
private Slider slider;
private Text aValueText;
private Text bValueText;
private Text aureaText;
private Ellipse ellipse;
private Paint falsePaint;
private Paint truePaint;
private Button solveButton;

public static void main(String[] args) {
Application.launch(SectioAureaApplication.class, args);
}

@Override
public void start(Stage stage) throws Exception {
stage.setTitle("Sectio Aurea");

model = new SectioModel();
model.setAValue(0);
model.setBValue(100.9);

slider = SliderBuilder.create().
min(model.getAValue()).
max(model.getBValue()).
showTickMarks(true).
showTickLabels(true).
majorTickUnit(20).
minorTickCount(3).
prefWidth(300).
blockIncrement(0.1f).
build();

aValueText = TextBuilder.create().
build();

bValueText = TextBuilder.create().
build();

aureaText = TextBuilder.create().
build();

solveButton = ButtonBuilder.create().
text("Solve").
build();

Stop[] falseStops = new Stop[]{new Stop(0.0, Color.WHITE), new Stop(0.3, Color.RED), new Stop(1.0, Color.DARKRED)};
falsePaint = new RadialGradient(0.0, 0.0, -10.0, -10.0, 50, false, CycleMethod.NO_CYCLE, falseStops);

Stop[] trueStops = new Stop[]{new Stop(0.0, Color.WHITE), new Stop(0.3, Color.GREENYELLOW), new Stop(1.0, Color.DARKGREEN)};
truePaint = new RadialGradient(0.0, 0.0, -10.0, -10.0, 50, false, CycleMethod.NO_CYCLE, trueStops);

ellipse = EllipseBuilder.create().
radiusX(30).
radiusY(30).
fill(falsePaint).
build();

DropShadow ds = new DropShadow();
ds.setOffsetY(3.0f);
ds.setColor(Color.color(0.4f, 0.4f, 0.4f));

ellipse.setEffect(ds);

Text titleText = TextBuilder.create().
text("Sectio Aurea").
effect(ds).
font(Font.font(null, FontWeight.BOLD, 32)).
build();

Text descriptionText = TextBuilder.create().
text("Move the slider until your hit the golden section.\nHint: use the cursor keys for exact control.").
textAlignment(TextAlignment.CENTER).
build();

// Put it all together
GridPane root = new GridPane();
root.setPadding(new Insets(20, 20, 20, 20));
root.setGridLinesVisible(false);

RowConstraints rowInfo = new RowConstraints(100);
ColumnConstraints colInfo = new ColumnConstraints(100);

root.getRowConstraints().
add(rowInfo);
root.getRowConstraints().
add(rowInfo);

for (int j = 0; j <= 2; j++) {
root.getColumnConstraints().
add(colInfo);
}

GridPane.setHalignment(titleText, HPos.CENTER);
GridPane.setHalignment(descriptionText, HPos.CENTER);
GridPane.setHalignment(slider, HPos.CENTER);
GridPane.setHalignment(aValueText, HPos.CENTER);
GridPane.setHalignment(ellipse, HPos.CENTER);
GridPane.setHalignment(bValueText, HPos.CENTER);
GridPane.setHalignment(solveButton, HPos.CENTER);

GridPane.setConstraints(titleText, 0, 0, 3, 1);
GridPane.setConstraints(descriptionText, 0, 1, 3, 1);
GridPane.setConstraints(slider, 0, 2, 3, 1);
GridPane.setConstraints(aValueText, 0, 3);
GridPane.setConstraints(ellipse, 1, 3);
GridPane.setConstraints(bValueText, 2, 3);
GridPane.setConstraints(solveButton, 2, 4);

root.getChildren().
addAll(titleText, descriptionText, slider, aValueText, ellipse, bValueText, solveButton);

bindProperties();

stage.setScene(new Scene(root, 350, 380));
stage.show();

}

private void refreshIndicator() {
if (model.getSectioAureaProperty().
get()) {
ellipse.fillProperty().
set(truePaint);

} else {
ellipse.fillProperty().
set(falsePaint);
}
}

private void bindProperties() {

model.getAValueProperty().
bindBidirectional(slider.valueProperty());

aValueText.textProperty().
bind(model.getAValueProperty().
asString("%.1f"));
bValueText.textProperty().
bind(model.getBValueProperty().
asString("%.1f"));

aureaText.textProperty().
bind(model.getSectioAureaProperty().
asString());

model.getSectioAureaProperty().
addListener(new ChangeListener() {

@Override
public void changed(ObservableValue arg0, Boolean arg1, Boolean arg2) {
refreshIndicator();
}
});

solveButton.setOnAction(new EventHandler() {

@Override
public void handle(ActionEvent arg0) {
model.setAValue(61.8);
}
});

}
}

_____________________________________________________

here’s the NetBeans project: SectioAurea.zip

Enter the theater

Stage

The User Interface in JavaFX is understood as a kind of drama or spectacle. Including a stage on which a scene is presented as the play. Parts of the scene are the actors and props.

javafx.scene.Stage

The application “acts” on the stage.
How this stage is presented to the audience depends on where the stage is established. E.g. as part of a WebPage, on the desktop or on a tablet.

Scene

javafx.scene.Scene

The scene is the play.

Node

javafx.scene.Node

Nodes are the actors and props of a scene.

All nodes in JavaFX are derived from the class javafx.scene.Node.
Specifically a node can be:

  • the various UI controls
  • any form (shape)
  • text (which is also a shape)
  • images
  • media player
  • embedded browser
  • individual UI controls

A Node can also be a (layout) container for other nodes.

A scene has a “scene graph” that represents all the nodes of the scene grouped under a common direction. Individual properties or entire node groups can be manipulated in various ways, e.g. be moved , scaled or colored.

UIs in JavaFX are totally node-centered.