iBreed: Part I
A new episode on my JavaFX UI series articles.
For the application I’m currently developing, I’d like to have a nice window decoration so I worked first on the “Undecorator” series. Now, I need to mix HTML5 content in my JavaFX app so I decided to create a light framework to facilitate this hybrid ecosystem.
So, please welcome: iBreed!
iBreed is a light framework for hybrid JavaFX/HTML5 applications.
Why hybrid?
To use the best of both worlds! Leverage the JavaFX platform (desktop integration, file system, back-end, Frameworks, gestures…) mixed with the HTML5 “universal” content.
The goal of iBreed
Mixing technologies is not a recommended approach. Now days, things are going fast and reuse instead of rewriting could be a tactical and interesting step.
And with JavaFX, this is possible ☺!
More than a mixing framework, it should be “fused”. Combining JavaFX and HTML technologies means bringing the same experience to the end-user: same look and feel, same fonts, unique drag and drop… i.e. the user interface must look uniform, no visual difference between HTML and JavaFX parts (e.g. no URL loading progress bar ☺!).
At current stage, here is what iBreed could bring to you:
1. A ready to run executable JAR to quickly test your HTML pages in a JavaFX context.
2. A WebView “injector” class with all implemented handlers (dialogs, Java Script bridge…)
3. A polished windowing system for hybrid applications.
4. A test HTML page with examples of interoperability (dnd, JS 2 Java…)
Note: Since I based the windows look on a flat style, I incorporated JMetro style as the root CSS of all Scenes (BTW, great work from Pixel Duke http://pixelduke.wordpress.com, available in JFXtras 2).
Try it out!
To Hybrid your App, take a look at the IBreedSkeleton class (https://github.com/in-sideFX/iBreed/blob/master/src/demoapp/IBreedSkeleton.java) that contains a minimal implementation. This “skeleton” provides an “undecorated” Stage with a customized WebView (WebViewInjector) fused with a basic JavaFX pane. Of course, include the iBreedDemo.jar (or get Undecorator.jar + iBreed.jar from repo) into your project.
public class IBreedSkeleton extends Application { @FXML private WebView webView; @FXML private TextField urlTxt; UndecoratorScene undecoratorScene; // The bridge object JavaScriptBridge javaScriptBridge; @Override public void start(final Stage stage) throws Exception { Pane root = null; // UI part of the decoration try { FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ibreedskeleton.fxml")); fxmlLoader.setController(this); root = (Pane) fxmlLoader.load(); } catch (Exception ex) { System.err.println(ex); } // Add Chrome undecoratorScene = new UndecoratorScene(stage, root); undecoratorScene.setFadeInTransition(); setAsHybrid(stage); stage.setTitle("iBreed"); stage.setScene(undecoratorScene); stage.sizeToScene(); stage.toFront(); stage.centerOnScreen(); /** * Fade transition on window closing request */ stage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent t) { t.consume(); // Do not hide undecoratorScene.setFadeOutTransition(); } }); // Set stage's sizes based on client area's sizes Undecorator undecorator = undecoratorScene.getUndecorator(); stage.setMinWidth(undecorator.getMinWidth()); stage.setMinHeight(undecorator.getMinHeight()); stage.setWidth(undecorator.getPrefWidth()); stage.setHeight(undecorator.getPrefHeight()); if (undecorator.getMaxWidth() > 0) { stage.setMaxWidth(undecorator.getMaxWidth()); } if (undecorator.getMaxHeight() > 0) { stage.setMaxHeight(undecorator.getMaxHeight()); } stage.show(); } /** * Customize the user interface for hybrid application * * @param stage */ public void setAsHybrid(Stage stage) { // The generic object for JS and JavaFX interop javaScriptBridge = new JavaScriptBridge(webView.getEngine()); javaScriptBridge.fromJSProperty.addListener(new ChangeListener<String>() { /** * Invoked when JavaScript sends a new message */ @Override public void changed(ObservableValue<? extends String> ov, String t, String t1) { System.out.println("Something changed on Java Script side..."); } }); // Inject WebView customizations (handlers...) WebViewInjector.inject(stage, webView, javaScriptBridge); // Default URL to load final String url = getClass().getResource("page_skeleton.html").toExternalForm(); webView.getEngine().load(url); // Reflect the current URL in the text field webView.getEngine().getLoadWorker().stateProperty().addListener( new ChangeListener<Worker.State>() { @Override public void changed(ObservableValue<? extends Worker.State> ov, Worker.State oldState, Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { urlTxt.setText(webView.getEngine().getLocation()); urlTxt.setStyle("-fx-background-color:white;;-fx-border-color: #bababa;-fx-border-width: 2px;-fx-border-style: solid;"); // urlTxt.setStyle("-fx-background-color:white;"); } else if (newState == Worker.State.FAILED) { urlTxt.setStyle("-fx-background-color:red;"); } } }); } /** * If user enters new url * * @param event */ @FXML private void onUrlTxtChanged(ActionEvent event) { String url = urlTxt.getText(); webView.getEngine().load(url); } public static void main(String[] args) { launch(args); } }
Visual result is:
IBreed demo
I also delivered a more complete example as a test “suite” for my hybrid application.
You can execute it by clicking on this icon:
This sample covers:
• The 3 main popup types (prompt, confirm, alert)
• The external “browser” window popup type
• Basic drag and drop
• The two ways communication (Java to JS and JS to Java).
And it looks like:
Video:
Takeaway
All sources and binaries are here: https://github.com/in-sideFX/iBreed/blob/master/
What’s next?
I’ll enrich iBreed by:
• Providing a consistent Look and feel (CSS) across the app: Stage, JavaFX Node’s skin, HTML CSS.
• Delivering more helpers (dnd…)
• Adding fancy effects for html page loading
• Fixing some “bugs” and performance issue on Windows using Java 8 (big fonts, Stage resize is slow…)
Thanks for reading and feel free to comment!
#1 by Mircea on 20/04/2013 - 08:48
That’s really nice. Good job.
#2 by move on 01/05/2013 - 03:05
Keep up the very good work.
#3 by Makis Teller on 19/07/2013 - 00:44
Excelent work, I will read your code trying to make it even better, you are inspiring me.
Continue this work and thanks you!
#4 by arnaud nouard on 19/07/2013 - 13:48
Thanks a lot, happy to read that !
May the best Developer win 🙂 !
A.