How to build a JRebirth Presentation

Last year I decided to create an application framework dedicated to JavaFX; the brand new graphical toolkit for Java.

After having played with Swing, SWT/JFaces, Flex, Silverlight and for sure Web API during several years I wanted to go back to Swing…
… and JavaFX 2 had been launched.

So I called my framework : JRebirth to beat all web-kiddies Up !!!

After few weeks of work, I had the opportunity to talk about JavaFX API during a ToulouseJug evening meeting.
As Office suite was not my best friends, filling excel and powerpoint files all the day is not my main objectives (whereas it ‘s currently my everyday life …).

So I decided to challenge myself and to create a Powerpoint’s like presentation entirely in JavaFX.
I activated the dog-fooding mode and I created the org.jrebirth.presentation sub module for JRebirth.

If you are a little bit lazy, you can fork one of the 4 presentations/applications I had built :
* ToulousejUg/Breizhcamp => [JavaFX Prez Source Code|https://github.com/JRebirth/JFX-Presentation]
* EclipseDay => [EclipseDay Source Code|https://github.com/JRebirth/EclipseDay-Presentation]
* ToulouseJug LightningTalk => [LightningTalk Source Code|https://github.com/JRebirth/LightningTalk]

Otherwise as JRebirth is based on Maven Tools; the best way to use is to create a Maven project.

Project configuration

Hereafter you can see a typical presentation folder hierarchy :

((/public/javafx2/.prez_tree_m.jpg|Presentation Application Tree Structure with JRebirth||Presentation Application Tree Structure with JRebirth, fév 2013))

The first step is to create your pom.xml, you can copy/paste the sample hereafter:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.jrebirth.presentation</groupId>
	<artifactId>lightningtalk</artifactId>
	<version>1.0.0</version>

	<name>JavaFX 2.2 Lightning Talk</name>
	<url>http://www.jrebirth.org/lightningtalk</url>
	<description>Interactive Short Presentation of JavaFX 2.2 capabilities</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

		<!-- Used for jnlp generation-->
		<jnlpFilename>LightningTalk.jnlp</jnlpFilename>
		<appClass>org.jrebirth.presentation.lightningtalk.LightningTalk</appClass>

		<appletWidth>1024</appletWidth>
		<appletHeight>768</appletHeight>

		<updateCheck>timeout</updateCheck>
		<updatePolicy>prompt-update</updatePolicy>

	</properties>

	<issueManagement>
		<system>GitHub Issue Tracker</system>
		<url>http://bugs.jrebirth.org</url>
	</issueManagement>

	<ciManagement>
		<system>Jenkins</system>
		<url>http://ci.jrebirth.org/job/LightningTalk-Trunk/</url>
	</ciManagement>

	<scm>
		<connection>scm:git:git://github.com/JRebirth/LightningTalk.git</connection>
		<developerConnection>scm:git:ssh://git@github.com/JRebirth/LightningTalk.git</developerConnection>
		<url>https://github.com/JRebirth/LightningTalk</url>
	</scm>

	<organization>
		<name>JRebirth Open Group</name>
		<url>http://www.jrebirth.org</url>
	</organization>

	<build>
		<resources>
			<resource>
				<filtering>false</filtering>
				<directory>${basedir}/src/main/java</directory>
				<includes>
					<include>**/*.fxml</include>
					<include>**/*.txt</include>
				</includes>
			</resource>
			<resource>
				<filtering>false</filtering>
				<directory>${basedir}/src/main/resources</directory>
				<includes>
					<include>**/*.*</include>
				</includes>
			</resource>
		</resources>

		<plugins>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>2.3.2</version>

				<configuration>
					<archive>
						<manifestEntries>
							<JavaFX-Version>2.0</JavaFX-Version>
							<Main-Class>${appClass}</Main-Class>
							<JavaFX-Application-Class>${appClass}</JavaFX-Application-Class>
							<implementation-vendor>seb</implementation-vendor>
							<implementation-title>Prez</implementation-title>
							<implementation-version>1.0</implementation-version>
							<JavaFX-Class-Path>jaxb-api-2.2.1.jar
								stax-api-1.0-2.jar activation-1.1.jar jaxb-impl-2.2.1.jar presentation-0.7.1-SNAPSHOT.jar core-0.7.1-SNAPSHOT.jar
								logback-classic-1.0.6.jar logback-core-1.0.6.jar
								slf4j-api-1.6.5.jar</JavaFX-Class-Path><!-- Optional (not required for JWS) Update it because it's not automated yet -->
						</manifestEntries>
						<manifest>
							<addClasspath>true</addClasspath>
						</manifest>
					</archive>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>webstart-maven-plugin</artifactId>
				<version>1.0-beta-3</version>

				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>jnlp</goal>
						</goals>
					</execution>
				</executions>

				<configuration>

					<jnlpFiles>${jrebirth.jnlp.filename}</jnlpFiles>
					<excludeTransitive>false</excludeTransitive>
					<libPath>lib</libPath>

					<resourcesDirectory>${project.basedir}/src/main/jnlp/resources</resourcesDirectory>
					<codebase>${deployUrl}/${deployPath}</codebase>

					<jnlp>
						<!-- <inputTemplateResourcePath>${project.basedir}</inputTemplateResourcePath> <inputTemplate>src/main/jnlp/template.vm</inputTemplate> -->
						<outputFile>${jnlpFilename}</outputFile>

						<mainClass>${appClass}</mainClass>
						<offlineAllowed>true</offlineAllowed>
					</jnlp>

					<sign>
						<keystore>${basedir}/jrebirth.jks</keystore>
						<keypass>${keypass}</keypass>
						<storepass>${storepass}</storepass>
						<!-- <storetype>fillme</storetype> -->
						<alias>jrebirth</alias>

						<validity>360</validity>
						<dnameCn>www.jrebirth.org</dnameCn>
						<dnameOu>None</dnameOu>
						<dnameO>JRebirth</dnameO>
						<dnameL>Toulouse</dnameL>
						<dnameSt>HG</dnameSt>
						<dnameC>FR</dnameC>

						<verify>true</verify>

						<keystoreConfig>
							<delete>true</delete>
							<gen>true</gen>
						</keystoreConfig>
					</sign>

					<pack200>true</pack200>
					<gzip>true</gzip>

					<outputJarVersions>false</outputJarVersions>
					<install>false</install>
					<verbose>true</verbose>
				</configuration>
			</plugin>

			<!-- Project Quality OPTIONAL 😀 -->
			<plugin>
				<groupId>org.codehaus.sonar</groupId>
				<artifactId>sonar-maven3-plugin</artifactId>
				<version>3.3.2</version>
			</plugin>
		</plugins>
	</build>

	<dependencies>

		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>com.sun.xml.bind</groupId>
			<artifactId>jaxb-impl</artifactId>
			<version>2.2.1</version>
		</dependency>

		<dependency>
			<groupId>org.jrebirth</groupId>
			<artifactId>presentation</artifactId>
			<version>0.7.2-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.0.6</version>
		</dependency>

	</dependencies>

</project>

The sample pom also allow you releasing your application with Java WebStart with a self-signed certificate.
You must fill 2 parameters to create your self-signed certificate :
* -Dkeypass= your pass
* -Dstorepass= your pass

This org.jrebirth:presentation dependency will fetch automatically the dependency: org.jrebirth:core which is the core of the JRebirth Application Framework.

Obviously you must connect to repo.jrebirth.org to download latest files, to do it you should read this page : [JRebirth Installation|http://www.jrebirth.org/installation.html]

Let’s code !

Now you have to create your package tree (for me org.jrebirth.presentation.lightningtalk), and add your first class.
This is your application main class and it must inherit from AbstractApplication class available into core library.
You can find more information about JRebirth Application class here : [Application|http://www.jrebirth.org/application.html]

/**
 * The class <strong>LightningTalk</strong>.
 * 
 * Application as support for live javafx 2.2 lightning talk.
 * 
 * @author Sébastien Bordes
 * 
 */
public final class LightningTalk extends AbstractApplication<StackPane> {

    /**
     * Application launcher.
     * 
     * @param args the command line arguments
     */
    public static void main(final String... args) {
        Application.launch(LightningTalk.class, args);
    }

    /**
     * {@inheritDoc}
     */
    // @Override
    public Class<? extends Model> getFirstModelClass() {
        return StackModel.class;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String getApplicationTitle() {
        return "JavaFX 2.2 - What's up ?";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void customizeStage(final Stage stage) {
        stage.setFullScreen(false);
        stage.setWidth(1040);
        stage.setHeight(800);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void customizeScene(final Scene scene) {

        scene.getStylesheets().add("style/template.css");

        // Manage Scene Zoom
        scene.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

            // @Override
            public void handle(final KeyEvent event) {
                if (event.isControlDown()) {
                    if (event.getCode() == KeyCode.ADD || event.getCode() == KeyCode.PLUS) {
                        getScene().getRoot().setScaleX(getScene().getRoot().getScaleX() + 0.05);
                        getScene().getRoot().setScaleY(getScene().getRoot().getScaleY() + 0.05);
                        event.consume();
                    } else if (event.getCode() == KeyCode.SUBTRACT || event.getCode() == KeyCode.MINUS) {
                        getScene().getRoot().setScaleX(getScene().getRoot().getScaleX() - 0.05);
                        getScene().getRoot().setScaleY(getScene().getRoot().getScaleY() - 0.05);
                        event.consume();
                    } else if (event.getCode() == KeyCode.DIGIT0 || event.getCode() == KeyCode.NUMPAD0) {
                        getScene().getRoot().setScaleX(1.0);
                        getScene().getRoot().setScaleY(1.0);
                        event.consume();
                    }
                }
            }
        });

        //Keep aspect ratio
        scene.getRoot().scaleXProperty().bind(Bindings.divide(getStage().widthProperty(), 1040));
        scene.getRoot().scaleYProperty().bind(Bindings.divide(getStage().heightProperty(), 800));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected List<FontItem> getFontToPreload() {

        return Arrays.asList(new FontItem[] {
                LtFonts.TYPEWRITER,
                LtFonts.DOG_SPLASH,
                LtFonts.WAZAA_SPLASH,
                LtFonts.SLIDE_ITEM
        });
    }

    /**
     * {@inheritDoc}
     */
    // @Override
    public List<Wave> getPreBootWaveList() {
        return Collections.emptyList();
    }

    /**
     * {@inheritDoc}
     */
    // @Override
    public List<Wave> getPostBootWaveList() {
        return Collections.emptyList();
    }

}

 

You must load the StackModel view by overriding getFirstModelClass() method to create a Presentation application.
This will create for you a customized view that will handle some events:
* Left click and PageDown to move forward into your slides stack
* Right click and PageUp to move backward into your slides stack
* Ctrl + ‘+’ to zoom in the scene
* Ctrl + ‘-‘ to zoom out the scene

JRebirth Core also adds these shortcuts (they could be changed by overriding methods too)
* F10 to maximize
* F11 for fullscreen mode

How to add content ?

You must create a presentation/Presentation.xml file compliant with the Presentation.xsd XML schema.
You can write raw text and create slides and sub-slides with image and hyperlink.
You should launch the application and compare the xml file to evaluate all features.
Here you have a full sample with a lot of different slides.

<?xml version="1.0" encoding="UTF-8" ?>
<presentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.jrebirth.org/Presentation" xsi:schemaLocation="http://www.jrebirth.org/Presentation Presentation.xsd">

	<title>JavaFX 2.2 What's Up ?</title>
	<date>15/11/2012</date>
	<event>ToulouseJug 11/2012</event>
	<place>Toulouse</place>

	<speakers>
		<speaker firstName="Sébastien" lastName="Bordes" email="contact@jrebirth.org" job="JRebirth" />
	</speakers>

	<slides defaultModelClass="org.jrebirth.presentation.lightningtalk.ui.slides.basic.BasicModel">

		<slide style="place" modelClass="org.jrebirth.presentation.lightningtalk.ui.slides.place.PlaceModel" showAnimation="FadeIn" hideAnimation="FadeOut">
		</slide>

		<!-- Presentation Title -->
		<slide style="intro" modelClass="org.jrebirth.presentation.lightningtalk.ui.slides.intro.IntroModel" showAnimation="FadeIn" hideAnimation="MoveToLeft">
			<title> JavaFX 2.2\n\t What's Up ?</title>
		</slide>

		<slide style="me" showAnimation="MoveFromLeft" hideAnimation="MoveToRight">
			<title>Sébastien Bordes</title>
			<content>
				<item>Développeur</item>
				<item level="1"></item>
				<item>Technologies</item>
				<item level="1">Swing, JEE, Eclipse RCP, Flash-Flex (AS3), Silverlight (C#)</item>
				<item></item>
				<item>@s8bordes</item>
				<item level="1">seb@jrebirth.org</item>
				<item link="true">www.jrebirth.org</item>
				<item link="true">blog.webeo.fr</item>
				<item></item>
				<item>Artwork by</item>
				<item level="1">xoxi@xoxi.fr</item>
			</content>
		</slide>

		<!-- Episode précédent -->
		<slide showAnimation="MoveFromBottom" hideAnimation="FadeOut">
			<title>Previously On ...</title>
			<content>
				<item>ToulouseJUG</item>
				<item level="1" link="true">www.jrebirth.org/toulousejug</item>
				<item>EclipseDay</item>
				<item level="1" link="true">www.jrebirth.org/eclipseday</item>
				<item>Breizhcamp</item>
				<item level="1" link="true">www.jrebirth.org/breizhcamp</item>
				<item>ToulouseJUG Lightning Talk</item>
				<item level="1" link="true">www.jrebirth.org/lightningtalk</item>
			</content>
		</slide>

		<slide style="dogfooding" modelClass="org.jrebirth.presentation.lightningtalk.ui.slides.intro.IntroModel" showAnimation="FadeIn" hideAnimation="FadeOut">
			<title>DogFooding.Mode = On</title>
		</slide>

		<!-- Read source -->
		<slide showAnimation="SlidingTopBottomProgressive" hideAnimation="FadeOut">
			<title>Explore it !</title>
			<content>
				<item>Source available on GitHub</item>
				<item link="true">https://github.com/JRebirth</item>
				<item></item>
				<item>Powered by JRebirth JavaFX Framework</item>
				<item link="true">www.jrebirth.org</item>
				<item image="images/me/JRebirth_128x128.png"></item>
			</content>
		</slide>

		<slide modelClass="org.jrebirth.presentation.ui.splash.SplashModel" showAnimation="FadeIn" hideAnimation="ScaleToMin">
			<title>So\n What's up ???</title>
		</slide>

		<slide showAnimation="MoveFromTop" hideAnimation="MoveToBottom">
			<title>Cross-Platform</title>
			<content>
				<item>JavaFX 2.2 is available on :</item>
				<item level="1">Windows (32-bit & 64-bit)</item>
				<item level="1">Mac OS X (64-bit)</item>
				<item level="1">Linux (32-bit & 64-bit)</item>
				<item>OpenJFX in OpenJDK</item>
				<item>Full Unified installer</item>
				<item level="1">Since Java SE 7u6</item>
				<item>jfxrt.jar included</item>
				<item level="1">Require some hack to generate Javadoc</item>
			</content>
		</slide>

		<slide showAnimation="ScaleFromMin" hideAnimation="ScaleToMin">
			<title>Packaging</title>
			<content>
				<item>Native packaging</item>
				<item level="1">.exe, .msi, bundle image, dmg, rpm</item>
				<item>Self-installable package</item>
				<item>Including Java & JavaFX Runtimes</item>
				<item level="1">Redistribution allowed</item>
				<item>... but still require ant :(</item>
			</content>
		</slide>

		<slide showAnimation="FadeIn" hideAnimation="MoveToBottom">
			<title>Event Handlers</title>
			<content>
				<item>Multi-Touch & Gesture</item>
				<item>Rotate</item>
				<item level="1">ROTATION_STARTED, ROTATE, ROTATION_FINISHED</item>
				<item>Scroll</item>
				<item level="1">SCROLL_STARTED, SCROLL, SCROLL_FINISHED</item>
				<item>Swipe</item>
				<item level="1">SWIPE_LEFT, SWIPE_RIGHT, SWIPE_UP, SWIPE_DOWN</item>
				<item>Zoom</item>
				<item level="1">ZOOM_STARTED, ZOOM, ZOOM_FINISHED</item>
			</content>
		</slide>

		<slide modelClass="org.jrebirth.presentation.ui.splash.SplashModel" style="kiosk" showAnimation="MoveFromTop" hideAnimation="MoveToBottom">
		</slide>

		<slide modelClass="org.jrebirth.presentation.lightningtalk.ui.slides.control.ControlModel" showAnimation="MoveFromTop" hideAnimation="MoveToLeft">
			<title>New controls</title>
			<content>
				<item>JFXtras 2</item>
				<item link="true">jfxtras.org</item>
				<item>Lot of control improvements</item>
				<item>JavaFX UI controls sandbox</item>
				<item link="true">hg.openjdk.java.net/openjfx/sandbox-8/controls/rt</item>
			</content>
		</slide>

		<slide showAnimation="MoveFromRight" hideAnimation="MoveToTop">
			<title>Media</title>
			<content>
				<item>WebView enhanced</item>
				<item>Read & Write pixels</item>
				<item>Enhanced font rendering on LCD displays</item>
				<item>MPEG-4 with H.264/AVC video and AAC audio codec</item>
				<item>HTTP Live Streaming with playlist</item>
			</content>
		</slide>

		<slide showAnimation="MoveFromBottom" hideAnimation="ScaleToMin">
			<title>Canvas</title>
			<content>
				<item>Canvas != FXCanvas</item>
				<item>GraphicsContext</item>
				<item>Draw & Fill methods (and more)</item>
				<item level="1">gc.setFill(Color.BLUE);</item>
				<item level="1">gc.fillRect(75,75,100,100);</item>
				<item>Save & Restore methods</item>
				<item level="1">equivalent to push & pop</item>
				<item>Effects & Transform</item>
				<item level="1">Canvas & Stack</item>
			</content>
		</slide>

		<slide modelClass="org.jrebirth.presentation.lightningtalk.ui.slides.fireworks.FireworksModel" showAnimation="ScaleFromMax" hideAnimation="ScaleToMax">
		</slide>

		<slide showAnimation="FadeIn" hideAnimation="ScaleToMin">
			<title>Integration</title>
			<content>
				<item>Swing integration documentation</item>
				<item>SWT integration documentation</item>
				<item></item>
				<item>New stable SceneBuilder 1.0</item>
				<item link="true">www.oracle.com/technetwork/java/javafx/tools</item>
				<item></item>
				<item>Scenic View, Visual Debugger</item>
				<item link="true">fxexperience.com/scenic-view/</item>

			</content>
		</slide>

		<slide showAnimation="FadeIn" hideAnimation="ScaleToMin">
			<title>Java 8 Overview</title>
			<content>
				<item>No more JavaFX version</item>
				<item></item>
				<item>Allow QuantumRenderer & JAT to run in parallel</item>
				<item></item>
				<item>Performance improvements :</item>
				<item level="1">more than 50% in Charts</item>
				<item level="1">up to 30% in DirtyArea</item>
				<item level="1">more than 100% in some Controls</item>
				<item level="1">between 20 to 100% in Guimark</item>
			</content>
		</slide>

		<slide modelClass="org.jrebirth.presentation.ui.image.ImageSlideModel" showAnimation="ScaleFromMax" hideAnimation="FadeOut">
			<content>
				<item image="images/splash/Merci.png" />
			</content>
		</slide>

		<slide modelClass="org.jrebirth.presentation.ui.image.ImageSlideModel" showAnimation="FadeIn" hideAnimation="FadeOut">
			<content>
				<item image="images/splash/Q_and_R.png" />
			</content>
		</slide>
	</slides>

</presentation>

How to add JavaFX content ?

For each slide XML element you can declare a modelClass which will be loaded and used according MVC principles of JRebirth Application Framework ([JRebirth Overview|http://www.jrebirth.org/overview.html]).
Your model should inherit from AbstractSlideModel.
Your view should inherit from AbstractSlideView.
Your controller should inherit from AbstractSlideController.

Customizable method are available into your view to trigger and stop you slide animation.

Example:

    /**
     * {@inheritDoc}
     */
    @Override
    protected void customInitializeComponents() {
        this.pane = new FireworksPane(loadImage("images/canvas/MontSaintMichel.jpg"), loadImage("images/canvas/MontSaintMichel.png"));
        StackPane.setAlignment(this.pane, Pos.CENTER);
        getRootNode().getChildren().add(this.pane);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void doStart() {
        doReload();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void doHide() {
        this.pane.stop();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void doReload() {
        this.pane.start();
    }

How to package ?

Your pom.xml allow to package it as a Java WebStart Application, so you can broadcast your slides online !
Hereafter you have some examples :
* [ToulouseJUG Demo|http://www.jrebirth.org/toulousejug.html]
* [EclipseDay Demo|http://www.jrebirth.org/eclipseday.html]
* [BreizhCamp Demo|http://www.jrebirth.org/breizhcamp.html]
* [LightningTalk Demo|http://www.jrebirth.org/lightningtalk.html]

Enjoy It and contact me on twitter ([@s8bordes|https://twitter.com/s8bordes]) if you have any question !

What’s next ?

I will probably add an archetype to build quickly a new fresh presentation application.
In the future I will add some JRebirth Services and Commands to manipulate Images and create some exciting animation,
this module is called org.jrebirt:transition and will be released soon … (depending if I have to create another presentation for another conference)

Currently I mainly work on JRebirth core new features.

Leave a Reply