JacpFX is a RCP framework based on JavaFX, supporting developers to structure an application with loosely coupled, reusable components and DataFX-flow is an API that allows you to define flows between controllers and views in JavaFX.
Both projects developed independently, and while I developed a JacpFX application I realized the lack of flow support. Integrating a simple DataFX-flow into JacpFX was quite easy, a JacpFX component is a controller with either a FXML view or a plain JavaFX view; on the other hand the FlowHandler of a DataFX-flow returns a StackPane which is easy to integrate in your current view. The drawback of this simple solution is the isolation of your DataFX-flow context from the JacpFX context which means, they have no direct access to each other.
At JavaOne I had the chance to spend a hour with Hendrik Ebbers (one of the DataFX committers) and to create a solution for this. The resulting JacpFX/DataFX-flow plugin allows you to inject the JacpFX context of a specific component to DataFX-flow controllers. It allows DataFX-flow controllers to communicate via the JacpFX message-bus with the rest of the JacpFX application. Beside this, the DataFX-flow controller gets access to resource-bundles and other methods/resources of the JacpFX component.
To demonstrate the usage of both frameworks I created a simple example application based on following steps:
JacpFX provides a simple archetype with two example perspectives (FXML + JavaFX view) and two components, reused in both perspectives. To create a basic JacpFX application use the simple archetype:
mvn archetype:generate -DarchetypeGroupId=org.jacpfx -DarchetypeArtifactId=JacpFX-simple-quickstart -DarchetypeVersion=2.0.2
To integrate the DataFX-flow plugin add following dependency to your pom:
<dependency>
<groupId>org.jacpfx</groupId>
<artifactId>jacpfx.JavaFXDataFXPlugin</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
Now we create two DataFX-flow controllers, a WizardStartController and a Wizard1Controller. The Wizard1Controller will contain the JacpFX context and include a textfield, text input will be send via messages to a JacpFX component. The controller will look like this:
@FXMLController(value="/fxml/wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller {
@FXML
@ActionTrigger("back")
private Button backButton;
@FXML
@ActionTrigger("next")
private Button nextButton;
/**
** The JacpFX context
**/
@Resource
private Context context;
@FXML
private TextField name;
@PostConstruct
public void init() {
nextButton.setDisable(true);
name.setOnKeyReleased(event->{
context.send(BasicConfig.COMPONENT_BOTTOM, name.getText());
});
}
}
To be able to inject a JacpFX context into a DataFX-flow controller I created a DataFX Flow-wrapper which additionally takes the ID of a JacpFX component who’s context should be injected. The usage of the DataFX Flow is exactly the same, so we create a FlowHandler and get the root node of the DataFX-flow when starting the FlowHandler. The root node can now be included to your JacpFX component view.
@View(id = BasicConfig.COMPONENT_TOP,
name = "SimpleView",
resourceBundleLocation = "bundles.languageBundle",
initialTargetLayoutId = BasicConfig.TARGET_CONTAINER_TOP)
public class ComponentLeft implements FXComponent {
private Node pane;
@Resource
private Context context;
@Override
public Node handle(final Message<Event, Object> message) {
// runs in worker thread
return null;
}
@Override
public Node postHandle(final Node arg0,
final Message<Event, Object> message) {
// runs in FX application thread
return this.pane;
}
@PostConstruct
public void onPostConstructComponent(final FXComponentLayout arg0,
final ResourceBundle resourceBundle) {
this.pane = createUI();
}
private Node createUI() {
final VBox main = new VBox();
Flow flow = new DataFXFlowWrapper(WizardStartController.class,BasicConfig.COMPONENT_TOP).
withLink(WizardStartController.class, "next", Wizard1Controller.class).
withGlobalBackAction("back");
FlowHandler flowHandler = flow.createHandler();
StackPane pane = null;
try {
pane = flowHandler.start(new AnimatedFlowContainer(Duration.millis(320), ContainerAnimations.ZOOM_IN));
main.getChildren().add(pane);
} catch (FlowException e) {
e.printStackTrace();
}
return main;
}
}
JacpFX and DataFX-flow needs Java 8 / JavaFX 8 to run, a packed jar you can download here: http://jacpfx.org/data/SimpleDataFX_JacpFX.jar , simply execute:
java -jar SimpleDataFX_JacpFX.jar
The source of this example application can be found here: https://github.com/amoAHCP/JacpFX-DataFX/tree/master/SimpleDataFX_JacpFX
The basics about DataFX-flow you can find on Hendriks blog here:http://www.guigarage.com/2014/05/datafx-8-0-tutorials/
The JacpFX homepage with all the documentation is http://jacpfx.org